做ACM练习题目已经两个年头了,最开始脑子很混乱,后来慢慢有了感觉,AC率也大幅度提高,接下来就一些经典问题做一下回顾,这些问题涵盖了算法的多个方面,相对来说有一些较为全面的总结,也是我这些年劳动成果的一些体现,这是第一篇,带来的是经典动态规划问题——最大子序列。
题目如下
Description
Given a set of n integers: A={a1, a2,..., an}, we define a function d(A) as below:
Your task is to calculate d(A).
Input
The input consists of T(<=30) test cases. The number of test cases (T) is given in the first line of the input.
Each test case contains two lines. The first line is an integer n(2<=n<=50000). The second line contains n integers: a1, a2, ..., an. (|ai| <= 10000).There is an empty line after each case.
Each test case contains two lines. The first line is an integer n(2<=n<=50000). The second line contains n integers: a1, a2, ..., an. (|ai| <= 10000).There is an empty line after each case.
Output
Print exactly one line for each test case. The line should contain the integer d(A).
Sample Input
1 10 1 -1 2 2 3 -3 4 -4 5 -5
Sample Output
13
Hint
In the sample, we choose {2,2,3,-3,4} and {5}, then we can get the answer.
Huge input,scanf is recommended.
Huge input,scanf is recommended.
题目分析
先来回顾下经典的一段最大子序列问题的最优子结构:
dp[i]=max(dp[i-1]+value[i],value[i]);
这个dp[i]代表的是以第i个字符为最后一个字符,且必须包含第i个字符的时候,最大的子序列和。
现在我们来看,这个题目要求的是两段不相交的子序列,这时候上面的公式就不起作用了。我们需要定义这样两个序列:
s[i],这个和上面那个dp[i]定义是一样的。
e[i],这个代表在前i个字符组成的字符串中选择最大的那个字符串。
e[i]的求法是这样的。先按照刚才的思路,求dp[i],
if(dp[i]>e[i-1]){
e[i]=dp[i];
}
else{
e[i]=e[i-1];
}
道理大家想一想应该就能出来。
接下来还需要一个数组 inverse[i],inverse代表反转,先把那个数列反转,然后求dp[i],就是inverse[i]。
可以这样理解,inverse[i]就是必须包括第i个字符,在i,i+1,。。。直到n个字符组成的字符串里选择最大的一个。
最后的结果当然就是在e[i]+inverse[i+1]中选择最大的那个!
代码如下:
#include<iostream>
using namespace std;
int n,num;
int d[50001];
__int64 dp[50001];
__int64 e[50001];
__int64 calc(){
dp[1]=d[1];
e[num]=d[num];
__int64 max=-99999999;
for(int i=2;i<=num;i++){
if(dp[i-1]>0){
dp[i]=dp[i-1]+d[i];
}
else{
dp[i]=d[i];
}
}
for(int i=2;i<=num;i++){
if(dp[i]<dp[i-1]){
dp[i]=dp[i-1];
}
}
for(int i=num-1;i>=1;i--){
if(e[i+1]>0){
e[i]=e[i+1]+d[i];
}
else{
e[i]=d[i];
}
}
for(int i=1;i<=num-1;i++){
if(dp[i]+e[i+1]>max){
max=dp[i]+e[i+1];
}
}
return max;
}
int main(){
cin>>n;
for(int cur=1;cur<=n;cur++){
cin>>num;
for(int i=1;i<=num;i++){
cin>>d[i];
}
cout<<calc()<<endl;
}
return 0;
}
按照上面要求,使用scanf和printf,这个代码用的是cin和cout所以有超时,改过就好,目的就在于学习到这种思想,而非一定要AC。