POJ 2479 动态规划 最大子序列问题(两段) Maximum sum

做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.

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.

题目分析

    先来回顾下经典的一段最大子序列问题的最优子结构:
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。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值