对于给定序列a1,a2,a3……an,寻找它的某个连续子段,使得其和最大。如( -2,11,-4,13,-5,-2 )最大子段是{ 11,-4,13 }其和为20。
要求:分别用教材所给的三种方法求解(简单方法、分治法、动态规划),通过实例比较结果和时间效率。
提交内容:算法的思想、程序源代码及其说明、实例运行结果及分
以a[0]开始: {a[0]}, {a[0],a[1]},{a[0],a[1],a[2]}……{a[0],a[1],……a[n]}共n个
以a[1]开始: {a[1]}, {a[1],a[2]},{a[1],a[2],a[3]}……{a[1],a[2],……a[n]}共n-1个
……
以a[n]开始:{a[n]}共1个
暴力破解方法简单直观,通过两层嵌套循环遍历所有可能的子段,计算它们的和,并保留最大和。这种方法的时间复杂度为O(n^2),适用于小规模数据集。
将序列a[1:n]分成长度相等的两段a[1:n/2]和a[n/2+1:n],分别求出这两段的最大字段和,则a[1:n]的最大子段和有三中情形:
a[1:n]的最大子段和与a[1:n/2]的最大子段和相同;
a[1:n]的最大子段和与a[n/2+1:n]的最大子段和相同;
a[1:n]的最大字段和为∑ak,且1<=i<=n/2,n/2+1<=j<=n。
可用递归方法求得情形1、2。对于情形3,可以看出a[n/2]与a[n/2+1]在最优子序列中。因此可以在a[1:n/2]中计算出
,并在a[n/2+1:n]中计算出 。则s1+s2即为出现情形3时的最优值。
????????
????????分治法采用递归的方式将问题划分成更小的子问题,分别求解子问题的最大子段和,然后合并得到整体的最大子段和。这种方法的时间复杂度为O(n log n),适用于中等规模的数据集。它的优势在于对于更大规模的问题,其效率相对较高。
D[i]表示从i开始的最大字段和。(但我们不是从前往后找字段结束位置)
根据递推公式,我们可知要想求得D[i],就必须知道D[i+1],所以我们从前往后计算。
如下图:以i=12开始的子段和D[12]=X[12]=-1,该子段结束位置Rec[12]=i=12;
当i=11时,D[11+1]<0,所以D[11]=X[11]=7,Rec[i]=i=11;
当i=10时,D[10+1]>0,所以D[10]=X[10]+D[11]=3+7,Rec=i+1=11;
…
一直到i,我们就找完了所有子段和,接着在子段和中找最大的。
????????动态规划采用自底向上的方式,通过迭代计算子问题的最优解,并将结果保存起来,避免了重复计算。这种方法的时间复杂度为O(n),适用于大规模数据集。它的优势在于具有更好的时间效率,但相对于分治法,实现稍显复杂。
源码:
//暴力破解
int sum1(int arr[],int len) {
??? int sum=0;
??? for (int i = 0; i < len; i++) {
???????? int this_sum = 0;
???????? for (int j = i; j < len; j++) {
???????????? this_sum += arr[j];
???????????? if (this_sum > sum) {
????????????????? sum = this_sum;
???????????? }
???????? }
??? }
??? return sum;
}
//分治法
int maxSubSum(int a[], int left, int right) {
??? int sum = 0;
??? if (left == right) {
???????? sum = a[left] > 0 ? a[left] : 0;
??? }
??? else {
???????? int center = (left + right) / 2;
???????? int leftSum = maxSubSum(a, left, center);
???????? int rightSum = maxSubSum(a, center + 1, right);
???????? int s1 = 0;
???????? int lefts = 0;
???????? for (int i = center; i >= left; i--) {
???????????? lefts += a[i];
???????????? if (lefts > s1) s1 = lefts;
???????? }
???????? int s2 = 0;
???????? int rights = 0;
???????? for (int i = center + 1; i < right; i++) {
???????????? rights += a[i];
???????????? if (rights > s2)? s2 = rights;
???????? }
???????? sum = s1 + s2;
???????? if (sum < leftSum) sum = leftSum;
???????? if (sum < rightSum) sum = rightSum;
??? }
??? return sum;
}
int maxSum(int a[], int n) {
??? return maxSubSum(a, 0, n - 1);
}
//动态规划
int sum2(int arr[],int len)
{
??? int sum=0;
??? int b=0;
??? int left=0;
??? int right=0;
??? for (int i = 0; i < len; i++) {
???????? if (b > 0) {
???????????? b += arr[i];
???????????? right = i;
???????? }
???????? else {
???????????? b = arr[i];
???????? }
???????? if (sum < b) {
???????????? sum = b;
???????? }
??? }
???
??? return sum;
}
实验分析:
测试了2000个数据,除了暴力破解比较慢用了8ms,其他的都不足1ms。