/* 最大连续和: 给出一个长度为n的序列A1,A2,...,An,求最大连续和。换句话说,要求找到1<=i <= j<=n,使得Ai+Ai+1+..+Aj尽量大 b[i] = { b[i-1] + a,b[i-1] > 0 {a,b[i-1] <0 输入: 8 1 -2 3 10 -4 7 2 -5 输出: 18(3 10 -4 7 2) */ /* 关键: 1 分治法3步骤:划分,递归,合并。 划分:元素二分 递归:分别求完全左半和完全右半最大连续和序列 合并:求出起点位于左半,终点位于右半的序列。先寻找最佳起点,再寻找最佳终点 本质:由于采用先划分再递归的方式,实际上是一个后递归问题 2 先求出两个子序列中的连续和较大值,再与本身最大子序列和的最大值进行比较,大者即为最大连续子序列和 3 if(high - low == 1)//递归出口 4 int mid = low + (high - low)/2;//分治第一步,划分,/取整方向是朝零,用x+(y-x)/2确保分界点靠近区间起点,技巧 5 int iMax = maxSum(iArr,low,mid) > maxSum(iArr,mid,high) ? maxSum(iArr,low,mid) : maxSum(iArr,mid,high);//分治第二步,递归,为后面比较子序列的最大值做准备 6 int iSum = 0,iL = iArr[mid-1];//我终于明白为什么从中间向两头求最大值,这样才能使两个区间的最大值进行相加,并且注意这里是从左端点的左边开展的,必须从iArr[mid-1]开始 7 for(int i = mid -1 ; i >= 0; i--)//注意,这里易错,是从mid-1开始,因为它把mid留给了左区间,从分界点向左求最大连续子序列和 { iSum += iArr[i]; if(iSum > iL) { iL = iSum; } } 8 for(int j = mid ; j < high;j++)//取不到high要注意 9 return iMax > (iL + iR) ? iMax : (iL + iR);//分治第三步,合并 10 递归思路:设序列长度为n的次数为T(n),则T(n) =2*T(n/2) + n,T(1)=1,T(n/2)是长度为n/2的递归调用,而最后的n是合并时间,这样T(n)=O(nlogn) 11 int maxSum(int* iArr,int low,int high)//返回数组在[x,y)中国的最大连续和,继续被分为[x,mid),[mid,y)进行再处理 12 多项式时间算法:时间复杂度为多项式的算法 指数时间算法:n!或2^n的算法 */ #include <stdio.h> #include <stdlib.h> #define MAXSIZE 1024 //动态规划法 void maxSum() { int n; while(EOF != scanf("%d",&n)) { int iArr[MAXSIZE]; for(int i = 0 ; i < n; i++) { scanf("%d",&iArr[i]); } int max = iArr[0]; int b = 0; for(int i = 0 ; i < n ; i++) { if(b > 0) { b += iArr[i]; } else { b = iArr[i]; } if(max < b) { max = b; } } printf("%d\n",max); } } //方法2:Ai+Ai+1 + ...+Aj = Sj - Si-1,连续子序列之和等于两个前缀之差 void maxSum_diff() { int n ; int iMax; int iCnt; while(EOF != scanf("%d",&n)) { int iArr[MAXSIZE]; for(int i = 0 ; i < n; i++) { scanf("%d",&iArr[i+1]);//注意。这里必须要从下标1开始赋值,因为它采用了A1,A2,...,An方式 } int i; int iSum[MAXSIZE]; iSum[0] = 0; iMax = iArr[1]; iCnt = 0; for(i = 1 ; i <= n;i++)//这里有n个元素,从1开始就能取到n,注意这个与下面的循环是不相同的 { //iSum[i] = iSum[i-1] + i; iSum[i] = iSum[i-1] + iArr[i]; } for(i = 1 ; i <= n; i++) { for(int j = i ; j <= n;j++) { if(iMax < iSum[j] - iSum[i-1])//这里由于要减去i-1,因此i从1开始,以n结束 { iMax = iSum[j] - iSum[i-1];//更新最大值 } iCnt++; } } printf("%d %d\n",iMax,iCnt); } } //分治法 int maxSum(int* iArr,int low,int high)//返回数组在[x,y)中国的最大连续和 { if(high - low == 1)//递归出口 { return iArr[low]; } int mid = low + (high - low)/2;//分治第一步,划分 int iMax = maxSum(iArr,low,mid) > maxSum(iArr,mid,high) ? maxSum(iArr,low,mid) : maxSum(iArr,mid,high);//分治第二步,递归,为后面比较子序列的最大值做准备 int iSum = 0,iL = iArr[mid-1];//我终于明白为什么从中间向两头求最大值,这样才能使两个区间的最大值进行相加,并且注意这里是从左端点的左边开展的,必须从iArr[mid-1]开始 for(int i = mid -1 ; i >= 0; i--)//注意,这里易错,是从mid-1开始,因为它把mid留给了左区间,从分界点向左求最大连续子序列和 { iSum += iArr[i]; if(iSum > iL) { iL = iSum; } } int iR = iArr[mid],iSum1 = 0; for(int j = mid ; j < high;j++)//取不到high要注意 { iSum1 += iArr[j]; if(iSum1 > iR) { iR = iSum1; } } return iMax > (iL + iR) ? iMax : (iL + iR);//分治第三步,合并 } void process() { int n; while(EOF != scanf("%d",&n)) { int iArr[MAXSIZE]; for(int i = 0 ; i < n;i++) { scanf("%d",&iArr[i]); } int iRes = maxSum(iArr,0,n); printf("%d\n",iRes); } } int main(int argc,char* argv[]) { //maxSum(); //maxSum_diff(); process(); system("pause"); return 0; }