[kuangbin带你飞]专题十二 基础DP1 A - Max Sum Plus Plus HDU - 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 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.

Sponsor

题目的大意就是给你n个数字,让你选取m个连续的数字子串,这些字串之间不能重叠,求怎么选取可以让m个数字字串的和数字和最大。
拿第二个示例来说明,给了6个数字: -1 4 -2 3 -2 3,选取2个连续子串。这里选取子串【4,-2,3】和【3】,两个字串的和为8是最大的。

解题思路

这是一道典型的动态规划的问题。动态规划问题求解时,最重要的就是要找状态方程。这道题的状态方程是d[i][j] = max( max(d[i-1][t]), d[i][j-1] ) + s[j]。首先解释d[i][j]的意思,d[i][j]代表找到i个子串,且以第j个数字结尾的最大值。d[i][j]可能由两种状态转移得到,第一种状态是s[j]会单独被当作一个字串,和前面的i-1个字串合起来就是i个字串。max(d[i-1][t])表示找到i-1个字串,且结尾的数字的个数是小于j的,其中t的范围是[i-1,j-1],这里的下界i-1是因为至少由i-1个数字才能组成i-1个字串。第二种状态是s[j]会加入以s[j-1]结尾的字串中,这种状态下字串个数不会增加,因此这里d[i][j-1]代表找到i个字串,且以第j-1个数字结尾的最大值。
我们比较两个状态 max(d[i-1][t])和d[i][j-1],取得值最大的那个状态然后加上s[j]。
这道题用文字描述很不容易理解清楚,我们用 https://blog.51cto.com/13688928/2117013这篇博客中画的表格来说明。
现在举一个例子,序列为-1,4,-2,3,-2,3。

在这里插入图片描述这个表格就是d[i][j]的表格。我们现在求d[2][4]也就是说有2个字串,并以第4个数字结尾的最大值。他的其中一个转移状态是 d[i][j-1],这里是d[2][3],也就是也就是说有2个字串,并以第3个数字结尾的最大值,从表格中我们可以看出来是d[2][3]是3,。他的第二个转移状态是 max(d[i-1][t]),1<=t<=3,也就是说要在下图中的红框内的三个值中选一个最大的,这里应该是4。两种转移状态的值都得到了,一个是3,一个是4,选择更大的4,然后与s[4],也就是3相加,得到7。那么d[3][4]就等于7。
在这里插入图片描述我们通过迭代,可以将d[i][j]都求出来,也就是说得到了这个表。当题目问这几个数字中选2个连续子串,子串和的最大值是多是时,我们就可以比较i=2这行,i=2这行的数字是3,2,7,5,8。其中8是最大的,那么我们就可以得出结论最大的两个连续字串和是8。

优化方法

写这道题的时候,我们可以按照上面的思路维护一个d[i][j]数组,然后将d[i][j]的所有值求出来后再找最大值。但是这样时间和空间复杂度都会很高,大家从表中可以看出来,这个表其实是一个三角矩阵,我们维护的d[i][j]数组一半的空间是没有用到的。因此我们在这里使用一维数组代替二维数组来优化空间。我们维护名为pre_d的一维矩阵。我们实际做的过程中是一行一行来迭代的,也就是说,先迭代i=1这行,然后迭代i=2这行。每次迭代的时候只要记住前一行的结果就能计算得到状态max(d[i][t]) 。第二个状态 d[i][j-1]其实用一个变量就能维护,因为我们迭代每一行的时候都会从左往右迭代,也就是说前一个迭代的数字就是 d[i][j-1],我们用一个变量来记录就可以了。但是我写代码的时候没有考虑到这一点,又用了一个数组d来记录当前迭代的行。大家可以参考别人的代码或者自己优化一下这一点。

AC代码

下面给出AC代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m;
int s[1000005];
int d[1000005];
int pre_d[1000005];
int main()
{
	int i,j,k,max_pre,max_now;
	while(scanf("%d%d",&m,&n)!=EOF)
	{
		memset(d,0,sizeof(d));
		memset(pre_d,0,sizeof(pre_d));
		for(i =1;i<=n;i++)
		{
			scanf("%d",&s[i]);
		}

		for(i = 1;i<=m;i++)
		{
			

			max_now = 0;
			for(k = 1;k<=i;k++) max_now +=s[k];
			max_pre = pre_d[i];
			d[i] = max_now;
			for(j = i+1;j<=n;j++)
			{
				d[j] = max(d[j-1],max_pre)+s[j];
				max_pre = max(max_pre,pre_d[j]);
				max_now = max(max_now,d[j]);
				pre_d[j] = max_now;
				
			}
		}
		cout<<max_now<<endl;
		
	}

 } 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值