HDU1024 Max Sum Plus Plus(最大字段和)

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

题意

求长度为n,节数为m的最大字段和。(大家可以先看下这篇滚动数组的文章

具体过程

通常动态规划,我们都是先考虑最后一个状态结果,看看能不能继续递推状态。

所以我们先根据题目,先推出最容易想到的一个状态。

我们考虑到j位置:

1、它要么属于当前循环组数中的第j个位置

2、要么就是新的一组中的头一个数字,但是前面的组中,我们需要考虑有多少个数字,i-1组的话,最少也要i-1个数字,最多可能有j-1个数字

简单画个图看看(假设此时i=2,j=4)

在这里插入图片描述

所以我们可以将最大字段和表示为dp[i][j],i代表第i组,j代表第j个位置

简单版递推式:dp[i][j]=max(dp[i][j-1]+a[j],max(dp[i-1][k]+a[j])),k属于[i-1,j-1]

for(int i=1;i<=m;i++)							//最原始的dp
{
	for(int j=1;j<=n;j++)
	{
		mx=-1;
		for(int k=i-1;k<=j-1;k++)
			mx=max(mx,dp[i-1][k]+a[j]);			//找出i-1到j-1位置中最大的那个
		dp[i][j]=max(dp[i][j-1]+a[j],mx);		//再与dp[i-1][j-1]+a[j]做比较
	}
} 

因为题目范围是1e6,二维数组,加三重循环,很明显会MLE

不过最起码的思路是有了,代码也十分的简洁,我们可以想方法来具体优化一下空间、时间复杂度。

优化后的递推式:dp[j]=max(dp[j-1]+a[j],pre[j-1]+a[j]))

首先我们由式子dp[i][j]=max(dp[i][j-1]+a[j],max(dp[i-1][k]+a[j])),k属于[i-1,j-1],

不管是前者还是后者,都是根据到j-1位置的最大字段和来计算的

我们完全可以用一个一维dp来存贮到j位置的最大字段和即可,即表示为dp[j]的形式。

我们又会发现,我们这里其实需要的只是上一行的数据,也就是dp[i-1][j-1]最大字段和的数据

我们完全可以在开一个pre数组来不断记录上一行的数据,再用mx记录下到j位置的最大字段和,

一旦使用完pre[j-1]中的数据,我们就用mx数据将其更新,达到滚动数组的效果

代码 + 相关注释

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e6+10;
const ll inf = 0x3f3f3f3f;

ll a[maxn],dp[maxn],pre[maxn],mx;

int main()
{
	ll n,m;
	while(scanf("%lld %lld",&m,&n)!=EOF)
	{
		for(int i=1;i<=n;i++)							//init
		{
			scanf("%lld",&a[i]);
			dp[i]=0;
			pre[i]=0;
		}
		for(int i=1;i<=m;i++)							//优化后的dp 
		{
			mx=-inf;
			for(int j=i;j<=n;j++)						//i组的话,至少需要i个数字
			{ 
				dp[j]=max(dp[j-1]+a[j],pre[j-1]+a[j]);	//调用pre,此时的pre[j-1]存贮的数据是max(dp[i-1][x],x属于[i-1,j-1]),
				pre[j-1]=mx;							//我们再将pre[j-1]更新为mx中存储的到j-1位置的最大字段和
				mx=max(mx,dp[j]);						//再将mx更新到j位置时最大的字段和
			}
		}
		printf("%lld\n",mx);							//因为dp[j]表示加上j位置时得到的最大字段和,而题意时是求m个字段的最大字段和
														//所以我们要判断的其实是max(dp[i][k]),k属于[i,j],故输出的是mx而不是dp[j]
	}
	return 0;	
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值