最大子段和
Posted wushengyang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最大子段和相关的知识,希望对你有一定的参考价值。
一. 问题适用方法
给定长度为n的整数序列,a[1....n],求 [1,n] 某个子区间 [i,j]使得 a[i]+.....+a[j] 和最大,或者求出最大的这个和。例如(-2,11,-4,13,-5,2)的最大子段和为20,所求子区间为 [2,4]。
二. 问题分析
1.穷举法
用两层for循环遍历所有的子区间。
//穷举法 #include<bits/stdc++.h> int start=0; //起始位置 int end=0; //结束位置 int max=0; for(int i=1;i<=n;i++){ for(int j=1;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.分治法
求子区间及最大和,从结构上是非常适合分治法的,因为所有子区间[start,end]只可能有以下三种可能:
1.在[1,n/2]这个区域
2.在[n/2+1,n]这个区域
3.起点位于[1,n/2],终点位于[n/2+1,n]
以上三种情况的最大值就是所求的,前两种符合子问题的递归性,所以可以递归,第三种则需要单独处理,第三种情形包括了 n/2,n/2+1两个位置,这样可以利用第二种穷举的思路求出:
1.以n/2为终点,往左移动扩张,求出和最大的一个left_max
2.以n/2+1为起点,往右移动扩张,求出和最大的一个right_max
3.left_max+right_max是第三种情况可能的最大值
int maxInterval(int a,int left,int right){ if(right==left) return a[left]>0?a[left]:0; int center=(left+right)/2; int leftMaxInterval=maxInterval(a,left,center); //左边区间的最大子段和 int rightMaxInterval=maxInterval(a,center+1,right); //右边区间的最大子段和 // 以下求端点分别位于不同部分的最大子段和 //center开始向左移动 int sum=0; int left_max=0; for(int i=center;i>=left;--i){ sum+=a[i]; if(sum>left_max) left_max=sum; } //center+1开始向右移动 sum=0; int right_max=0; for(int i=center+1;i<=right;i++){ sum+=a[i]; if(sum>right_max) right_max=sum; } int ret=lefr_max+right_max; if(ret<leftMaxInterval) ret=leftMaxInterval; if(ret<rightMaxInterval) ret=rightMaxInterval; return ret; }
这种算法的时间复杂度为 O(nlogn)
3.动态规划法
由于是一个连续的区间,所以可以这样思考
1.令b[j]表示以位置 j 为终点的所有子区间中和最大的一个。
2.子问题:如j为终点的最大子区间包含了位置 j-1,则以 j-1 位终点的最大子区间必然包括在其中。
3.如果 b[j-1]>0,那么显示 b[j]=b[j-1]+a[j],用之前最大的一个加上a[j]即可,因为a[j]必须包含。
4.如果 b[j-1]<=0,那么b[j]=a[j],因为既然最大,前面的负数必然不能使你更大。
//动态规划 int max=0; int b[n+1]; int start=0,end=0; memset(b,0,n+1); for(int i=1;i<=n;i++){ if(b[i-1]>0){ b[i]=b[i-1]+a[i]; } else{ b[i]=a[i]; } if(b[i]>max) max=b[i]; }
时间复杂度 O(n)。
以上是关于最大子段和的主要内容,如果未能解决你的问题,请参考以下文章