最大连续子序列和
最大连续子序列和问题
给定一个序列,求该序列的所有连续子序列中和最大值
例{1,2,3,-9,3,-1,9,-3}
和最大的连续子序列是{3,-1,9},最大和为11
这个题目按照动态规划的思路求解。我们假设和最大连续子序列是以输入序列的第k个数结尾的input[k],那么这个序列也一定是从input[0]到input[k]这k个数的序列中的和最大的连续子序列。以例题来看,和最大的连续子序列是{3,-1,9},其结束的数字是原序列的第7个数,那么这个子序列必然是{1,2,3,-9,3,-1,9}这个序列以最后一个数字结尾的和最大连续子序列。
那么我们考虑把从序列中第0到第n-1位总共n个数结尾的和最大子序列都找出来,然后把对应的和存到一个长度为n的数组中,这其中最大的那个数一定就是连续子序列的最大和。
还是以例题来说明。
input={1,2,3,-9,3,-1,9,-3}
以第1个数结尾的连续子序列的最大和就是input[0],也就是1,记做sum[0]=1
以第2个数结尾的连续子序列的最大和就是sum[0]+input[1],也就是3
以第3个数结尾的连续子序列的最大和就是sum[1]+input[2],也就是6
以第4个数结尾的连续子序列的最大和就是sum[2]+input[3],也就是-3,这里需要注意,这里我们找的是以第4个数结尾的子序列,那么必须要包含第四个数,尽管前面三个数的和比-3要大,但只有前面三个数时不包含第四个数,所以以第四个数结尾的和最大的连续子序列是{1,2,3,-9}
以第5个数结尾的连续子序列的最大和就是input[5],也就是3,这里需要注意,由于前面四个数的和小于0,所以不论input[5]是多少,sum[4]+input[5]都要比input[5]小,所以sum[5]=input[5]
以第6个数结尾的连续子序列的最大和就是sum[5]+input[6],也就是2
以第7个数结尾的连续子序列的最大和就是sum[6]+input[7],也就是11
这样我们就得到了一个sum数组{1,3,6,-3,3,2,11},这里最大的是最后一个数11,所以原序列的和最大的连续子序列一定是以最后一个数结尾的序列,最大和是11。
从上面的分析,我们可以得到一个动态规划的递推关系
具体的代码实现如下
int maxsubarray(vector<int> &input)
{
vector<int> sum(input.size(),0);
int tmp=0;
for(int i=0;i<input.size();i++)
{
if(tmp<0) tmp=input[i];
else tmp+=input[i];
sum[i]=tmp;
}
return *max_element(sum.begin(),sum.end());
}
测试一下
int main()
{
vector<int> input(10,0);
cout<<"{";
srand(clock());
for(auto p=input.begin();p!=input.end();p++)
{
*p=rand()%20;
if(rand()%2) *p=-*p;
cout<<*p;
if(p!=input.end()-1) cout<<",";
}
cout<<"}\n";
cout<<maxsubarray(input)<<'\n';
return 0;
}
结果为
{14,6,5,-9,-4,19,-9,2,15,-3}
39
可以看到这个序列的连续子序列的最大和是第一个数到倒数第二个数组成的子序列,最大和是39,结果是正确的。
但其实这个算法还可以进一步优化,实际上并不需要真的去开辟sum这个数组,只需要保留一个最大和maxsum并且在累加过程中不断更新这个maxsum就行了。具体做法如下
int maxsubarray(vector<int> &input)
{
int maxsum=input[0],tmp=0;
for(int i=0;i<input.size();i++)
{
if(tmp<0) tmp=input[i];
else tmp+=input[i];
if(tmp>maxsum) maxsum=tmp;
}
return maxsum;
}
同样测试一下
int main()
{
vector<int> input(10,0);
cout<<"{";
srand(clock());
for(auto p=input.begin();p!=input.end();p++)
{
*p=rand()%20;
if(rand()%2) *p=-*p;
cout<<*p;
if(p!=input.end()-1) cout<<",";
}
cout<<"}\n";
cout<<maxsubarray(input)<<'\n';
cout<<maxsubarray1(input)<<'\n';
return 0;
}
结果为
{18,19,11,16,15,-17,9,-14,12,7}
79
结果同样是正确的。