题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1003
题目描述:
就是给你一个数组,需要你找出最大的连续子序列的和,和起始位置
如:(6,-1,5,4,-7),这样一个数组,最大的连续子序列就是6+(-1)+5+4=14,起始点为:1,结束点为:4,故输出14,1,4
然后我这里再说几个特殊的例子:
0 6 0
输出的是6 1 2
6 -1 1
输出的是6 1 1
6 -7 -8 14
输出的是14 4 4
解题思路
这道题需要用到动态规划。我们定义dp[100005]数组,第n个的dp即dp[n]就是代表着前n个的子序列的最大值。
状态转移方程:dp[1] = a[1];
dp[i] = max(dp[i-1]+dp[i],dp[i])【n>=2时执行】
如果dp[n-1]是小于0的,那么就代表着前面的n-1个的和是负数,那么可以抛弃掉前面n-1个,用temp来记录新的起点位置。
如果dp[n]>max就将dp[n]赋值个max,即max = dp[n]【保证max始终是最大值】同时也要更新起始和结束点。比如:6 -1 5 4 -7
首先,先将里面的所有的数都存入dp数组里面
然后,再初始化dp[1]=6,start=1,end=1,max=dp[1]=6的值
然后从2循环到5
dp[2] = 6+(-1) = 5 小于max不做操作
dp[3] = 5+5 = 10大于max,更新max,start和end的值
dp[4] = 10 + 4大于max,更新max,start和end的值
dp[5] = 10 +(-7)小于max不做操作【注】当dp[n-1]<0的时候需要用temp,而不是直接用start来接收。
这是为了避免出现:6 -6 -2 -3 -4这种情况,如果j从第3个循环就开始小于0了,
并且一直到5结束为止,如果直接在里面写成start = j 的话会导致start = 5是错误答案
而当dp[n]>max才执行更新,保证了这个start的值的正确性
ac代码:
#include<stdio.h>
#include<string.h>
#define N 100005
int main(){
int dp[N];
int max;
int start,end;
int T;
int n;
int temp;
scanf("%d",&T);
for(int i = 1;i <= T;i++){
memset(dp,0,sizeof(dp));
scanf("%d",&n);
for(int k = 1;k <= n;k++){
scanf("%d",&dp[k]);
}
start = 1;
end = 1;
max = dp[1];
temp = 1;//记录起点,当这第j个数的dp[j]>max时才更新到start上去,
for(int j = 2;j <= n;j++){
if(dp[j-1] >= 0){//证明前面一个是正数,或者是前面的部分加起来是正数
dp[j] = dp[j] + dp[j-1];
}else{//前面加起来是负数,就不能作为起点,得选这个作为新的起点
temp = j;//这是为了避免出现:6 -6 -2 -3 -4这种情况,如果j从第3个循环就开始小于0了,
//并且一直到5结束为止,如果直接在里面写成start = j 的话会导致start = 5是错误答案
}
if(dp[j] > max){//证明当前的数大于最大值
max = dp[j];
end = j;
start = temp;
}
}
if(i>=2){
printf("\n");
}
printf("Case %d:\n",i);
printf("%d %d %d\n",max,start,end);
}
return 0;
}