一、一段最长连续子序列之和问题:
给定一个数字序列:A1,A2 …… An,求i,j(i<=j<=n),使得Ai + Ai+1 + …… + Aj最大,输出最大和。
样例:-2 11 -4 13 -5 -2
输出:20
子问题:取一段连续子序列,要求是该序列和最大
思路:用dp[i]表示 1到 i 的最大序列和,而dp[i]的值与dp[i-1]和A[i]有关,dp[i] = max(dp[i-1]+A[i],A[i]);
memset(dp,-inf,sizeof(dp));
dp[0] = a[0];
for(int i=1;i<n;i++){
dp[i] = max(dp[i-1]+A[i],A[i]);
}
int ans = dp[0];
for(int i=1;i<n;i++){
if(dp[i]>ans){
ans = dp[i];
}
}
cout<<ans<<endl;
二、两段最长连续子序列之和问题
POJ 2479 Maximum sum
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.
题意:给出一组数列,求出数列中最大的两个不相交的子段和。
思路:对于每一个 i 来说,都有两段:[0, i ) 和 [i, n ) 。我们分别求出每个 i 对应这两段的最大子段和,然后遍历所有的i,得到最大值。
lp[1]=a[1];
for(int i=2;i<=n;i++)
lp[i]=max(a[i],lp[i-1]+a[i]);
此时得到的 lp[i] 是包含i的最大子序列和。
for(int i=2;i<=n;i++)
lp[i]=max(lp[i-1],lp[i]);
此时 lp[i] 为i左边(包含i这个点)的最大子序列和。
AC代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
const int inf = 0x3f3f3f3f;
const int maxn = 50010;
using namespace std;
int lp[maxn],rp[maxn];
int a[maxn];
int main(void)
{
int t;
cin>>t;
while(t--){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",a+i);
}
lp[1]=a[1];
for(int i=2;i<=n;i++)
lp[i]=max(a[i],lp[i-1]+a[i]);
for(int i=2;i<=n;i++)
lp[i]=max(lp[i-1],lp[i]);
rp[n]=a[n];
for(int i=n-1;i>=1;i--)
rp[i]=max(a[i],rp[i+1]+a[i]);
for(int i=n-1;i>=1;i--)
rp[i]=max(rp[i+1],rp[i]);
int ans = -inf;
for(int i=2;i<=n;i++){
ans = max(ans,lp[i-1]+rp[i]);
}
printf("%d\n",ans);
}
return 0;
}
三、m段最长连续子序列之和问题
[HDOJ 1024 Max Sum Plus Plus](http://acm.hdu.edu.cn/showproblem.php?pid=1024)
Problem Description
Now I think you have got an AC in Ignatius.L’s “Max Sum” problem. To be a brave ACMer, we always challenge ourselves to more difficult problems. Now you are faced with a more difficult problem.
Given a consecutive number sequence S1, S2, S3, S4 … Sx, … Sn (1 ≤ x ≤ n ≤ 1,000,000, -32768 ≤ Sx ≤ 32767). We define a function sum(i, j) = Si + … + Sj (1 ≤ i ≤ j ≤ n).
Now given an integer m (m > 0), your task is to find m pairs of i and j which make sum(i1, j1) + sum(i2, j2) + sum(i3, j3) + … + sum(im, jm) maximal (ix ≤ iy ≤ jx or ix ≤ jy ≤ jx is not allowed).
But I`m lazy, I don’t want to write a special-judge module, so you don’t have to output m pairs of i and j, just output the maximal summation of sum(ix, jx)(1 ≤ x ≤ m) instead.
Input
Each test case will begin with two integers m and n, followed by n integers S1, S2, S3 … Sn.
Process to the end of file.
Output
Output the maximal summation described above in one line.
Sample Input
1 3 1 2 3 2
6 -1 4 -2 3 -2 3
Sample Output
6
8
Hint
Huge input, scanf and dynamic programming is recommended.
题意:给出一组数列,求出数列中最大的m个不相交的子段和。
思路:题解参考自https://www.cnblogs.com/dongsheng/archive/2013/05/28/3104629.html动态规划的思想
1.基本思路:
首先,定义数组a[n],dp[m][n];
a[n]用来储存n个整数组成的序列;
dp[i][j]用来表示由前 j 项得到的含 i 个子段的最大值,且最后一个子段以a[j]结尾。可以得出:
因为必须是以a[j]结尾,所以a[j]一定是属于最后一个子段,既要么自己独立成一个子段,要么与前边以a[j-1]为结尾的子段连接
所以dp[i][j] = max(dp[i][j-1]+a[j],dp[i-1][t]+a[j]) 其中 i-1 <= t <= j-1.
所求的最后结果为max(dp[m][j])其中1<=j<=n;
但是我们会发现,当n非常大时,这个算法的时间复杂度和空间复杂度是非常高的,时间复杂度近似为O(mn2),空间复杂度近似为O(mn)。因此,我们需要优化算法来降低时间复杂度和空间复杂度
2.优化算法:
(1)节省时间
由基本思路,我们可以知道,dp[i][j] = max(dp[i][j-1]+a[j],dp[i-1][t]+a[j]) 其中 i-1 <= t <= j-1.我们只要找到dp[i][j-1]和dp[i-1][t]的最大值,再加上a[j]就得到dp[i][j]。
所以定义一个数组mdp[n],用mdp[j-1]来表示求解dp[i][j]时,dp[i-1][t]的最大值,则dp[i][j] = max(mdp[j-1],dp[i][j-1]) + a[j];
特别注意,mdp[n]这个位置的储存空间是始终用不到的,因此可以用来储存其他数值,在接下来会用到。
在求解dp[i][j]的同时,我们可以计算出dp[i-1][t],(i<=t<=j)的最大值,这个最大值在计算dp[i][j]的时候需要做为mdp[j]的形式用到,我们先把它存放在mdp[n]中。
你可能会问:为什么不把它直接存放在mdp[j]中呢?因为你接下来需要计算dp[i][j+1]的值,需要用到mdp[j]中原来的值。如果你把它存在这里就会覆盖计算dp[i][j+1]所需要的值。所以把它放在mdp[n]中。
当我们计算完dp[i][j+1]后,就会发现mdp[j]中的值已经没有用处了,我们可以把它更新为计算dp[i+1][j+1]所需要的那个值,即之前放在mdp[n]中的那个值,即执行mdp[j]=mdp[n].这样我们就节省了计算最大值时付出的时间代价。
(2)节省空间
通过时间的节省,我们突然间发现程序执行结束后mdp[n]的值即为最后的结果,mdp[n]数组才是我们希望求解的,dp[m][n]这个庞大的数组已经不是那么重要了,因此,我们现在用整型数ans来代替dp[m][n],用来临时存储dp[i][j]的值,作为求解mdp[n]的中介。这样就节省了dp[i][j]占用的极大的空间.
AC代码:
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
const int maxn = 1000005;
using namespace std;
int n, m;
int a[maxn],mdp[maxn];
int DP()
{
for(int i=1;i<=m;++i){
int ans = 0;
for(int k=1;k<=i;++k)
ans += a[k];
mdp[n] = ans; //初始化
for(int j=i+1;j<=n;++j){
ans = max(mdp[j-1],ans) + a[j];
mdp[j-1] = mdp[n];
mdp[n] = max(mdp[n],ans);
}
}
return mdp[n];
}
int main()
{
while(~scanf("%d%d", &m, &n)){
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
mdp[i] = 0;
}
printf("%d\n", DP());
}
return 0;
}