最大子段和问题
来源:互联网 发布:西南交大希望学院网络 编辑:程序博客网 时间:2024/05/21 06:29
最大子段和问题
参考博客:博客链接
一.问题描述
N个整数组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的连续子段和的最大值。
int start = 0;//起始位置int end = 0; //结束位置int max = 0;for(int i = 1; i <= n; i++){ for(int j = i; j <= n; j++) { int sum = 0; for(int k = i; k <=j; k++) { sum += a[k]; } if(sum > max) { start = i; end = j; max = sum; } }}
这个算法的时间复杂度是O(n^3)。当然,这个代码还可以优化,实际上我们并不需要每次都重新从起始位置求和加到终点位置.可以充分利用之前的计算结果.
或者我们换一种穷举思路,对于起点 i,我们遍历所有长度为1,2,…,n-i+1的子区间和,以求得和最大的一个.这样也遍历了所有的起点的不同长度的子区间,同时,对于相同起点的不同长度的子区间,可以利用前面的计算结果来计算后面的.
比如,i为起点长度为2的子区间和就等于长度为1的子区间的和+a[i+1]即可,这样就省掉了一个循环,计算时间复杂度减少到了O(n^2).代码如下:
int start = 0;//起始位置int end = 0;//结束位置int max = 0;for(int i = 1; i <= n; ++i){ int sum = 0; for(int j = i; j <= n; ++j) { sum += a[j]; if(sum > max) { start = i; end = j; max = sum; } }}2.分治法
分治算法一般分为如下三个步骤:
(1)划分问题:把问题的实例划分成子问题
(2)递归求解:递归解决子问题
(3)合并问题:合并子问题的解得到原问题的解
在本例中,“划分”就是把序列分成元素个数尽量相同的两半;“递归求解”就是分别求出完全位于左半或完全位于右半的最佳序列;“合并”就是求出起点位于左半、终点位于右半的最大连续和序列,并和子问题的最优解比较。
前两部分没有什么特别之处,关键在于“合并”步骤。既然起点位于左半,终点位于右半,则可以人为地把这样的序列分成两部分,然后独立求解:先寻找最佳起点,然后再寻找最佳终点。
求子区间及最大和,从结构上是非常适合分治法的,因为所有子区间[start, end]只可能有以下三种可能性:
- 在[1, n/2]这个区域内
- 在[n/2+1, n]这个区域内
- 起点位于[1,n/2],终点位于[n/2+1,n]内
以上三种情形的最大者,即为所求. 前两种情形符合子问题递归特性,所以递归可以求出. 对于第三种情形,则需要单独处理. 第三种情形必然包括了n/2和n/2+1两个位置,这样就可以利用第二种穷举的思路求出:
- 以n/2为终点,往左移动扩张,求出和最大的一个left_max
- 以n/2+1为起点,往右移动扩张,求出和最大的一个right_max
- left_max+right_max是第三种情况可能的最大值
int maxsum(int *A,int x,int y)//返回数组在左闭右开[x,y)中的最大连续和{ int v,L,R,maxs; if(y-x==1)return A[x];//只有一个元素直接返回 int m=x+(y-x)/2;//分治第一步:划分成[x,m),[m,y) int maxs=max(maxsum(A,x,m),maxsum(A,m,y));//分治第二步:递归求解 int v,L,R; v=0;L=A[m-1];//分治第三步:合并(1)——从分界点开始往左的最大连续和 for(int i=m-1;i>=x;i--) { L=max(L,v+=A[i]); } v=0,R=A[m];//分治第三步:合并(2)——从分界点开始往右的最大连续和 for(int i=m;i<y;i++) { R=max(R,v+=A[i]); } return max(maxs,L+R);//把子问题的解与L和R比较}3.动规
动态规划主要讨论这个问题的建模过程和子问题结构.时刻记住一个前提,这里是连续的区间
- 令b[j]表示以位置 j 为终点的所有子区间中和最大的一个
- 子问题:如j为终点的最大子区间包含了位置j-1,则以j-1为终点的最大子区间必然包括在其中
- 如果b[j-1] >0, 那么显然b[j] = b[j-1] + a[j],用之前最大的一个加上a[j]即可,因为a[j]必须包含(以j为终点)
- 如果b[j-1]<=0,那么b[j] = a[j] ,因为既然要最大,前面的负数必然不能使之更大
int maxx=0;for(int i=1; i<=n; i++){ if(dp[i-1]>0) { dp[i]=dp[i-1]+a[i]; } else dp[i]=a[i]; maxx=max(maxx,dp[i]);}
- 最大子段和问题
- 最大子段和问题
- 最大子段和问题
- 最大子段和问题
- 最大子段和问题
- 最大子段和问题
- 最大子段和问题
- 最大子段和问题
- 最大子段和问题
- 最大子段和问题
- 最大子段和问题
- 最大子段和问题
- 最大子段和问题
- 最大子段和问题
- 最大子段和问题
- 最大子段和问题
- 最大子段和问题
- 最大子段和问题
- gcc4.9.2源码编译
- C#下使用XmlDocument详解
- ImageLoader
- 关于 urllib 与 urllib2的区别
- hive中的分析函数以及时间戳的使用
- 最大子段和问题
- java selenium 环境搭建
- Java 注解
- python基础语法
- 手把手教你用7行代码实现微信聊天机器人 -- Python wxpy
- Java项目经验
- 11.16第十二课
- 从概念到案例:初学者须知的十大机器学习算法
- 51nod 1829 函数 容斥原理