最大字段问题-含最大子矩阵和m子段和
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
最大子段和问题
问题描述:给定由n个整数(可能为负整数)组成的序列
a1,a2,…,an,求该序列形如 ai,ai+1,…,aj i,j=1, …,n,i≤j 的子段和的最大值。当所有整数均为负整数时定义其最大子段
j 和为0。依此定义,所求的最优值为: max 0, max a k 1i j n k i
8
3、动态规划算法
分治法减少了各分组之间的一些重复计算,但由于分解后的问题 不独立,在情形(3)中重复计算较多,还是没有充分运用前期的计 算结果。动态规划的特长就是解决分解的子问题不独立的情况。 • 动态规划的思路就是通过开辟存储空间,存储各子问题的计算结 果,从而避免重复计算。其实就是用空间效率去换取时间效率。
17
参考代码
public static int MaxSum(int a[][],int m,int n){ int sum = 0; int b[] =new int[n]; for(int i = 0;i < m;i++){ for(int k = 0;k < n;k++) b[k] = 0; for(int j = i;j <m;j++){ for(int k = 0;k < n;k++) b[k]+=a[j][k]; int max = MaxSubsum(b,n); if(max > sum) sum = max; } } return sum; } 由于MaxSubsum()需要 O(n)的时间,估此算法 的双重for循环需要
1 j1 j 2 n
13
如图所示,在二维最大子段和问题中,我们要求的是这 样一个子矩阵,如图中红框所示,其中 0 i j m-1 , 0 p q n-1。
14
例子1: 0 -2 -7 0 99 2 -6 2 2 -4 1 -4 1
例子2: 1 3 -2 -5 -5 1 7 7
面的值。b[j]=b[j-1]+a[j],当b[j-1] 0时,前面子段的和对总 和没有贡献,要重新累加, b[j] =a[j]。由此可得计算b[j]的动 态规划递归式b[j]=max{b[j-1]+a[j],a[j]}, 1 j n。
10
举例:
k a[k] 1 3 2 -4 3 2 4 10 5 1
b[k]
3
-1
2
12
13
其中: b[1]=a[1]=3, b[2]=b[1]+a[2]=-1, b[3]=a[3]=2, b[4]=b[3]+a[4] =12, b[5]=b[4]+a[5] =13; 因此,对于数组a 而言,最大子段和为b[5], 即sum=13。
11
程序实现:
public static int MaxSubsum(int a[],int n){ int sum = 0,b = 0; 这已经是一个简化后的算法,最基本的 for(int i=0;i<n;i++){ 做法其实可以申请一个b[n]数组,每次 if(b>0){ 求解b[j]=max{b[j-1]+a[j],a[j]} ,最后 b+=a[i]; } 遍历b[n]求得最大数组即可。 else b = a[i]; if(b>sum){ sum = b; } } 动态规划法的计算时间复杂度为O(n) return sum; }
最大子段和问题
钱能武
030130733
1
讲课的主要内容:
• • • • • • 问题描述 最大子段和问题的简单算法以及改进算法(枚举/穷举) 最大子段和问题的分治算法 最大子段和问题的动态规划算法 推广1:最大子矩阵问题 推广2:最大m字段和问题算法及改进算法
补充内容:动态规划算法步骤 1、找出最优解的性质,并刻画其结构特征 2、递归地定义最优值 3、以自底向上的方式计算最优值 4、根据计算最优值时得到的信息结构最优解
12
子段和问题的扩展—2维最大子段和
二维最大子段和问题又称为最大子矩阵问题,给定一 个m行n列的整数矩阵a,试求矩阵a的一个子矩阵,使 其各元素之和为最大。 即 s (i1, i 2, j1, j 2) a[i ][ j ]
i i1 j j1 i2 j2
最大子矩阵和问题的最优值为 1maxm s(i1, i 2, j1, j 2) i1i 2
6
对于情形(3)。容易看出,序列元素a[(n/2)]与 a[(n/2)+1]一定在最优子序列中。因此,可以计算出
a[i:(n/2)]的最大值s1;并计算出a[(n/2)+1:j]中的最大值
s2。则s1+s2即为出现情形(3)时的最优值。据此可设计 出求最大子段和的分治算法。
7
算法如下:
int maxsum(int a[], int left, int right) s1 = 0; //处理情形(3) { lefts = 0; int sum = 0,leftsum,rightsum; for (i = center; i >= 1; i--) int i,j; { lefts += a[i]; int lefts,rights; if (lefts > s1) s1 = lefts; } int s1,s2; s2 = 0; int center; rights = 0; if (1 == n) 复杂度分析 for (j = center + 1; j <= n; j++) { { rights += a[j]; if (a[1] > 0) O(1) n c if (rights > s2) s2 = rights; } sum = a[1]; T (n) sum = s1 + s2; 2T (n / 2) O(n) n c if (sum < leftsum) else sum = 0; } sum = leftsum; T(n)=O(nlogn) else if (sum < rightsum) { sum = rightsum; center = (1 + n) / 2; } leftsum = maxsum(a,1, center); return sum; rightsum = maxsum(a,center + 1, }*/ n);
• 动态规划有很强的阶段递推思想,用前一段存储的计算结果,递
推后一阶段的结果,是一种全面继承前期信息的方法。
9
算法设计:
记 sum为a[1] ~a[j]的最大子段和,记b[j]为当前子段和。
即
b[ j ] max{ a[k ]}
1 i j k i j
,1 j n。
当b[j-1]>0时,前面子段的和对总和有贡献,所以要累加前
算法说明: 1、算法中的thissum代表 当前子段和,即a[i]到a[j]多 有元素的和;sum代表函数 结束时存储的最大子段和。 besti代表最大子段和的起 点下标,bestj代表代表最 大子段和的终点下标。 2、时间复杂度为O(n3).
4
改进的枚举算法设计
public static int MaxSubsum(int a[]){ int sum = 0; int besti; int bestj; for (int i=0;i<a.length;i++) { int thissum=0; for (int j=i;j<a.length;j++) { thissum+=a[j]; thissum+=a[j]; if (thissum>sum) { sum=thissum; besti=i+1; bestj=j+1; } } } return sum; }
5
由 i ak a j i ak 知第k次计算 k k
j
Baidu Nhomakorabea
j 1
的的和可由k-1次的结果
递推。 算法1每次都从 头开始累加,则可将算 法中的最内层一个for循 环省去,避免重复计算。
改进后的算法只需要O(n2)的计算时间
2、分治算法
经过以上改进只是减少了i一定时的重复计算操 作。其中仍会有很多重复计算。从这个问题结构可以 看出,它适合于用分治法求解。 如果将所给的序列a[1:n]分为长度相等的2段 a[1:n/2]和a[n/2+1:n],分别求出这2段的最大子段和, 则a[1:n]的最大子段和有3种情行。 (1)a[1:n]的最大子段和与a[1:(n/2)]最大子 段和相同; (2)a[1:n]的最大子段和与a[(n/2)+1:n]最大 子段和相同; 情形(1)、(2)可递归求得。 (3)a[1:n]的最大子段和为 , 且1≤i≤n/2,(n/2)+1≤j≤n。
①当m=1时,
则该问题变为求最大字段和的问题
②当m>1时
设b(i,j)表示前j个元素(必定包含第j个元素)分为互不相交的i段所 得的最大i子段和并且i<=j。 (注:b(i,j)不一定是最优最大i子段和) 因此在考虑第j个元素时,可能存在两种情况: 1)第j个元素和前一个元素一起包含在第i段中; 2)第j个元素独自划分在第i段中。 根据问题的分析,两种情况分别如下: 1)b(i,j-1)表示第j-1个元素的最大j子段和,所以b(i,j)=b(i,j-1)+a[j]. 2)max{b(i-1,k)}其中k=i-1..j-1.即表示为在第j个元素之前得到的 i-1个子段之和的最优选择。所以b(i,j)=max{b(i-1,k)+a[j]},其中k=i1..j-1. 综上:b(i,j)=max{b(i,j-1)+a[j],max b(i-1,k)+a[j]},其中k=i-1..j-1.
-4 6 6 9 9
其最大子矩阵为: 9 2
其最大子矩阵为: -5 6
其元素总和为11。
7 9
其元素总和为17。
动态规划法:
动态规划法其实就是把二维最大子段和转化为 一维最大子段和问题。 转化方法: 我们把这个矩阵划分成n个“条”,条的长度为1到 m,通过两个for遍历所有长度的条。 然后,若干个连续的条,就是一个子矩阵了,这样 问题就轻易地转化为一维最大子段和问题了。 通过求所有这种条,起点为i,长度为1到m-i+1的 “条”的最大子段和,就可以求出整个矩阵的最大 子矩阵了。
O(m2n)的计算时间
特别的:
当m= O(n)时算法需 要O(n3)计算时间
18
子段和问题的扩展—最大m字段和
最大m子段和问题:给定由n个整数(可能为负)组 成的序列a1、a2、a3...,an,以及一个正整数m,要求确定 序列的m个不相交子段,使这m个子段的总和最大! 例如:序列为 2 3 -7 6 4 -5 其中3个字段应该是 {2、3} 和为:15 若要求的m=3 {6} { 4}
例如: 11,-4,13 A=(-2,11,-4,13,-5,-2) 最大子段和为:
a
k 2
4
k
20
3
1、枚举算法设计
首先用最简单的枚举算法来解决这个问题。枚举所有可能的 起始下标和终止下标,累加求和。并从中选取最大的字段和。
• • • • • • • • • • • • • • • • public static int MaxSubsum(int a[]){ int sum = 0; int besti; int bestj; for (int i=0;i<a.length;i++) { for (int j=i;j<a.length;j++) { int thissum=0; for (int k=i;k<=j;k++) thissum+=a[k]; if (thissum>sum) { sum=thissum; besti=i+1; bestj=j+1; } } } return sum; }
b(i,j)=max{b(i,j-1)+a[j],max b(i-1,k)+a[j]},其中k=i-1..j-1. 例: a[1] a[2] a[3] a[4] a[5] a[6] a: 2 3 -7 6 4 -5
16
具体枚举长条的时候,同一起点的长度,由于“条”的不 同长度间可以利用之前的结果。 比如令b[k][i][j]表示第k个长“条”区间从i到j的和,那么 b[k][i][j+1] = b[k][i][j]+a[j][k]。 当然,实际编程的时候,由于之前的结果求完一维最大子 段和后,便不需要保存,所以只需要一维数组b即可。