最长连续子序列
题目大意:求一数组中最大的连续子段的和,要求输出最大和,并且输出这个子段开始的值和结尾的值。
如果有两个子段都是最大和,输出开始下标和结尾下标最小的那个。
分析:经典的动态规划题目。 这个问题简单点的思路就是枚举所有的区间段,取个最大值即可,复杂性O(N*N*N),优化一下O(N*N) ,还有分治方法。这里想重点写一下动态规划的方法,说一下自己的一点理解。
在我看来这其实就是个决策的过程,之所以这样思考,不得不提一下无后效应。对于某个给定的阶段状态来说,它以前的各个阶段的状态无法直接影响到它未来的决策,而只能够间接的通过当前的这个状态来影响。现假设数组 A[1:i - 1] 的 最大子段和 maxsum 已经求出,在面对A[i] 这个数时该怎么选择? A[1:i ] 的最大子段和显然取决于A[1:i - 1] 的maxsum 和 当前的A[i] ,当前的A[i] 能否形成一个新的最大子段则取决于A[1:i - 1] 中包含A[i-1] 的那个最大子段和,不妨假设其为tempsum,则有
maxsum{A[1:i]} = max {A[i] , tempsum+ A[i], maxsum{A[1:i-1]};据此从前往后递推即可求出maxsum{A[1:N]}. 但是这里还要求出子段的开始值和结尾值。这里不得不思考一下什么样的情况会改变子段的开始位置和终止位置。在我们遇到A[i] 的时候都要考虑一下tempsum,若tempsum < 0 则此时开始值就有必要改变一下,因为从当前的位置开始必然要比从前面某个位置开始的子段和要大,因为tempsum是小于0的,但这个改变并不是最终的开始值。还要考虑下这个A[i] 所带来的改变能否大于A[1:i-1] 的maxsum。若能,则开始位置和终止位置都要改变。否则子段的位置不变。
此外,这里还要处理数组值全为负数的情况。
代码:
#include<stdio.h>
int main()
{
int n;
int buf[10001];
while(scanf("%d",&n)!=EOF)
{
if(n == 0)
break;
int i,maxsum,start,end;
bool flag = true;
for(i = 0; i < n; i++)
{
scanf("%d",&buf[i]);
if(buf[i] >= 0)
flag = false;
}
if(flag)
{
printf("%d %d %d\n",0,buf[0],buf[n-1]);
continue;
}
else
{
maxsum = buf[0];
start = 0;
end = 0;
int itemp = 0;
int tempsum = buf[0];
for(i = 1; i < n; i++)
{
if(tempsum < 0)
{
tempsum = 0;
itemp = i;
}
tempsum += buf[i];
if(tempsum > maxsum)
{
maxsum = tempsum;
start = itemp;
end = i;
}
}
}
printf("%d %d %d\n",maxsum,buf[start],buf[end]);
}
return 0;
}