Description
Given a sequence a[1],a[2],a[3]......a[n], your job is to calculate the max sum of a sub-sequence. For example, given (6,-1,5,4,-7), the max sum in this sequence is 6 + (-1) + 5 + 4 = 14.
Input
The first line of the input contains an integer T(1<=T<=20) which means the number of test cases. Then T lines follow, each line starts with a number N(1<=N<=100000), then N integers followed(all the integers are between -1000 and 1000).
Output
For each test case, you should output two lines. The first line is "Case #:", # means the number of the test case. The second line contains three integers, the Max Sum in the sequence, the start position of the sub-sequence, the end position of the sub-sequence. If there are more than one result, output the first one. Output a blank line between two cases.
Sample Input
2 5 6 -1 5 4 -7 7 0 6 -1 1 -6 7 -5
Sample Output
Case 1: 14 1 4
Case 2: 7 1 6
这是一个动态规划题,可使用dp[i]表示以a[i]结尾的的最大子段和,那么这就是一个子结构,如何保证其最优性呢?
可列出状态转移方程为dp[i]=max(dp[i-1]+a[i],a[i]),如果前面的子段和是负数,那么就无需加上它,因为这对于a[i]来说是一种拖累
#include <iostream> #include <cstdio> using namespace std; const int Max=1e5+10; const int inf=0x3f3f3f3f; struct node { int l,val; }dp[Max]; int main() { int T,ca=1; for(scanf("%d",&T);T;T--) { int n,tmp,ans,pre,post; ans=-inf; scanf("%d",&n); dp[0].val=0;dp[0].l=1; for(int i=1;i<=n;i++) { scanf("%d",&tmp); if(dp[i-1].val+tmp>=tmp) { dp[i].val=dp[i-1].val+tmp; dp[i].l=dp[i-1].l; } else { dp[i].val=tmp; dp[i].l=i; } if(dp[i].val>ans) { ans=dp[i].val; pre=dp[i].l; post=i; } } printf("Case %d: %d %d %d ",ca++,ans,pre,post); if(T-1) puts(""); } return 0; }
尽管这道题可以使用dp方程形式去解决,也可以用一般方法去观察
6 -1 5 4 -7
sum ans
6 6
5 5
10 10
14 14
7 7
我们把数列从i=1处累加,每次取出ai处的累加值,最后得到了数列的最大子段和14,然而这样的方法并非总是正确的,例如
-2 5 -10 7 7 4
sum ans
-2 -2
3 3
-7 -7
0 0
7 7
11 11
很显然,答案是18而非11,说明累加并不一定是从i=1开始的,假如程序能够察觉应该从i=4处开始累加,那么答案就会正确了
那么程序需要实现两个功能,起点更新和答案更新。(这样之所以正确在于它枚举了所有区间,类似于尺取法)
起点的特性是什么呢?那个sum值不能是负数,正确性显然,如果加上一个负数值反而会变小,注意并不是说ai不能为负数,如(6 -1 5)
由此可知,当一个sum值为负时,就应该更新起点,把sum值置为0。然而应该把起点位置置为此时的i吗?
答案是NO,注意,由于累加的特性,此时的ai就是导致累加值为负的罪魁,(此时的ai肯定也是负数),如果将它算进去,就违背了之前的讨论。
还存在一个问题:起点更新和答案更新应该谁前谁后?
考虑到一种情况,此时的答案是负数,如果起点更新在答案更新之前,那么相当于人为地增加了1个0作为错误干扰信息,使得答案变为0
所以答案更新必须在起点更新之前,这一点在写dp时一样重要
#include <iostream> #include <cstdio> using namespace std; const int Max=1e5+10; const int inf=0x3f3f3f3f; int main() { int T,ca=1; for(scanf("%d",&T);T;T--) { int n,tmp,ans,sum,pre,anpre,anpost; ans=-inf; scanf("%d",&n); pre=1;sum=0; for(int i=1;i<=n;i++) { scanf("%d",&tmp); sum+=tmp; if(sum>ans) { ans=sum; anpost=i; anpre=pre; } if(sum<0) { sum=0; pre=i+1; } } printf("Case %d: %d %d %d ",ca++,ans,anpre,anpost); if(T-1) puts(""); } return 0; }