问题:(HDOJ_1024)
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:
1 3 1 2 3
2 6 -1 4 -2 3 -2 3
sample out
6
8
问题描述:
这道题与 HDOJ_1003是类似的。1003那道题是求单独一串最大公共子串和,这里是求多段规定数量的最大字串和。
思路:
假设序列为:[-1, 4, -2, 3, -2, 3]
ps:
dp[i] [j] :表示当序列长度为j时,分成 i 份 的最大和
那么当只求1对分割子串和时:
dp [1] [1] : -1;
dp[1] [2] : max(-1 , 4);
dp [1] [3]: max (-2 , max(-1, 4)+(-2)) —> 当以a[3]为字串结尾时,是a[3] 独自的值大还是与前面字串的相加后的值大。
dp[4]: max(3,dp[1][3] + 3) —> 当以a[4]为字串结尾时,是a[4] 独自的值大还是与前面字串的相加后的值大。
…
在这个过程中我们只要记录这几个a[n]的最大值maxv,就可以在知道主串的最大公共子串和。
有这个思路我们可以想一下,那么如果以 a[k] (0<K<n) 结尾时,这时候的maxv记录的就是a[1] 到 a[k]之间的最大公共子串的和!
那么我们通过一个数组 maxarry [ 1 ] [ ] 将每个maxv的结果记录下来!
接下来我们看
那么当求2对分割子串和时:
[-1, 4, -2, 3, -2, 3]
(要分割成 i 块,那被分割的长度肯定要大于等于 i 的啦!)
dp[ 2] [ 2] : 4+(-1)
dp [2] [3] :
当被割序列为a[3]结尾时,即在包含了a[3]的基础上切割,那么a[3]肯定且必须存在,所以要么a[3] 自己自成一对,要么要和前面某段子串合并起来成为一对。
-
当 a [3] 自成一对时:
那么a[3]前序列就要分担分割出(2-1)对的任务。 欸,a[3]前序列分割1对的最大结果我们不是已经算过了吗?不就是maxarry [2] 嘛! -
当 a[3] 与 前面某段子串合并起来成为一对:
要合并成一对,那 a[2 ] 是不是必须存在! 如果不存在那a[3] 不独自成一对了吗? 那么包含有a[2] 的最大公共字串和的值 不就是dp [ 2] [ 2] 吗? !
所以 我们只要取这两种情况的中最大那一个就ok了
dp[2] [3] : max ( dp[2] [2] + a[3] , maxarry[ 2] + a[ 3 ] )
同理的对于
dp[ 2 ] [ 4 ] :
-
a [4] 独自成为一对时: maxarry [ 3] + a [ 4 ]
-
a[ 4] 与前面的序列组合,再分割 : dp[ 2] [ 3 ] + a [ 4 ]
a[ 2 ] [ 4 ] = max( dp [ 2] [ 3 ] + a [ 4 ] , maxarry [ 3] + a [ 4 ] )
…之后都同理;
在这个过程中,依然要记得每个以 a[k] (1<=K<=n) 结尾时的最大值maxv,并把这些值存入maxarry [2] [ k ],这样就得到了当被割序列为全长时,割成 2 对时的最大值 即最终的maxv值。
在要求割3对时,已经有了割2对的最好结果 只需考虑 a [ k ]要不要自成一队的问题
那么要求割4,5,6… n对时,都说是如此的,在前面计算的最大值的基础上来的。所以使用动态规划就是这么来的。
优化:
可以看到上面过程中分割 i-1 的结果只是存在maxarry中,所以割 i 次的结果与dp [i-1] [ k ] 没有关系.
那么我们就不许需要用二位数组存储,只需要用一维数组存储就可以了
yi
同样的maxarry [ i ] [ k ] 的使用 与 maxarry [ i - 1] [ k ]没有关系,就不需要浪费空间了 用 maxarry [ k ] 一维就够了
代码:
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
int dp[1000000];
int maxarry[1000000];
int a[1000000];
int main()
{
int n,m;
while(~scanf("%d%d",&m,&n))
{
for(int i= 1;i<=n;i++)
{
scanf("%d",&a[i]);
}
memset(dp,0,sizeof(dp));
int maxv = -9999999;
memset(maxarry,0,sizeof(maxarry));
for(int i=1;i<=m;i++)
{
maxv = -9999999;
for(int j =i ;j<=n;j++)
{
dp[j] = max(dp[j-1]+a[j],maxarry[j-1] + a[j]);
maxarry[j-1] = maxv;
maxv = max(dp[j],maxv);
}
}
printf("%d\n",maxv);
}
感悟:
其实就是1003 题的深化吧, 最终要输出的时maxv ,而不是dp [ i ] [ j ]!