hdu-1024 Max Sum Plus Plus(DP+滚动数组)

1.题目

题目链接。题意大概就是,把一个数组分成m段, 有式子 sum(i1, j1) + sum(i2, j2) + sum(i3, j3) + … + sum(im, jm),求使得上述和最大,ik,jk是连续的jk和ik+1可以不连续。

2.分析

这是动态规划的题目,假设状态d[i][j]表示在选取第j个数字的情况下,将前j个数字分成i组的最大和。

则它的值有两种可能

①(x1,y1),(x2,y2)…(xi,yi,num[j])

②(x1,y1),(x2,y2)…(xi-1,yi-1),…,(num[j]),其中yi-1是第k个数字

(1)第j个元素被划分在了第i段,这个转移就很简单了dp[i][j]=d[i][j-1]+num[j].这个转移应该是很显而易见的吧。就不多说了。那么第二种情况呢?
(2)第j个元素没有被划分在第i段内。既然第j个元素没有被划分在第i段内,说明前j-1个元素被划分成了i-1段。那么这里就有一个问题,被划分成i-1段至少需要i-1个元素,最多j-1个元素。(因为当前只有j-1个元素可用)。所以这里并不是简单的dp[i][j]=dp[i-1][j-1]+num[j],需要枚举从i-1开始,到j-1结束这些状态,到底多少个元素划分成i-1段是最大的。

故:d[i][j]=max(d[i][j-1],d[i-1][k])+num[j],其中k=i-1,i,…,j-1

根据题目分析可以写出以下代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3;//这里的maxn其实远不止1e3,应该是1e6级别
const int INF = 1e8;
int dp[maxn][maxn];
int num[maxn];
int main()
{
	int n,m;
	//freopen("data.txt","r",stdin);
	while(~scanf("%d%d",&m,&n))
	{
		for(int i=1;i<=n;i++){
			scanf("%d",&num[i]);
		}
		memset(dp,0,sizeof(dp));
		for(int i=1;i<=m;i++){
			for(int j=i;j<=n;j++){
				int last=-INF;
				for(int k=i-1;k<=j-1;k++){
					if(dp[i-1][k]>last) last=dp[i-1][k];
				}
				dp[i][j]=max(dp[i][j-1],last)+num[j];
			}
		}
		cout<<dp[m][n]<<endl;
	}
	return 0;
 
} 

但是题目中,1 ≤ x ≤ n ≤ 1,000,000,内存会爆掉,而且时间复杂度为O(n3),也会TLE,时间与空间均需要优化

3.优化

注意到,d[ i ][ * ]只和d[ i ][ * ],d[ i-1 ][ * ]有关,即当前状态只与前一状态有关,可以用滚动数组完成。 (空间优化)
如果对滚动数组不太了解,可以点击看一下这篇博客

考虑我们也不需要j-1之前的最大和具体发生在k位置,只需要在j-1处记录最大和即可,用pre[j-1]记录即可,具体操作见代码,可以省去一个for。(时间优化)

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
const int INF = 1e9;
int d[maxn];
int pre[maxn];
int num[maxn];
int main()
{
	int m,n,tmp;
	//freopen("data.txt","r",stdin);
	while(~scanf("%d%d",&m,&n)){
		for(int i=1;i<=n;i++){
			scanf("%d",&num[i]);
		}
		memset(d,0,sizeof(d));
		memset(pre,0,sizeof(pre));
	
		for(int i=1;i<=m;i++){
			tmp=-INF;
			for(int j=i;j<=n;j++){
				d[j]=max(d[j-1],pre[j-1])+num[j];
				pre[j-1]=tmp;
				tmp=max(d[j],tmp);
			}
		}
		cout<<tmp<<endl;
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值