[题解]HDU1024 Max Sum Plus Plus

前言

最近困惑于线性\(dp\)的定义——只有状态表示为一维的才叫线性\(dp\)吗?
通过CSDN上的这篇文章,我得到了答案:

所谓线性\(dp\),就是递推方程是有一个明显的线性关系的,一维线性和二维线性甚至多维都有可能。
动态规划里的每一个状态都是一个多维(\(1-n\)维)的状态。
比如说背包问题就是一个二维的问题,如果我们把它画出来的话会是一个二维矩阵的形式。
而我们在求的时候,有一个明显的求的顺序:即一行一行地来求。这样的有线性顺序的叫做线性\(dp\)

正文

HDU 1024

这道题是一道很巧妙的线性\(dp\)题,在上一篇文章——线性\(dp\)模型中也提到过,因为其前身其实就是上一篇写到的「最大连续子段和」。只不过这一题问的不是一段,而是\(m\)段,所以较上一题我们的选择更加自由。

(HDU注册上让验证手机号,结果愣是一直没收到验证码,而一天最多只能发\(3\)次,所以暂时没法测试自己的代码,但是目前和正解对拍是对的)

题意简述

多测,每次给你\(n\)个数,要求从中选出\(m\)个没有公共部分的连续子段,求这几个连续子段的和的最大值。

数据范围:\(1\leq m\leq n\leq 10^6\)(奈何\(m\)和数据组数具体范围都没给,因为\(m=10^3,n=10^6\)这个数量级就已经不可做了)

解题思路

\(dp[i][j]\)表示以第\(j\)个元素结尾,分成\(i\)组的最大和\((i\leq j)\)。你可能会疑惑为什么不把\(i\)\(j\)的含义反过来。别急,这是为了后期的优化。

我们用下列样例演示:
输入:

2 6
-1 4 -2 3 -2 3

输出:

8

如图,表示\(6\)个元素,取出\(2\)组的\(dp\ table\)(其实\(2\)行就够了,不过为了更好地演示我画到了\(n\))。

如图所示,递推式为\(dp[i][j]=max(dp[i][j-1],dp[i-1][k])+a[j](1\leq k<j)\),下面是推导过程:

  • 上一个元素在当前组,因为在当前组,所以必须是连续的,即只有\(dp[i][j-1]\)
  • 上一个元素在上一组,而上一组可以和自己连续也可以不连续,所以有\(dp[i-1][k](1\leq k<j)\)
  • 上面两种情况取个最大值,别忘加上自己(即\(a[i]\))。

我们发现每个格子的状态只和当前行和上一行有关,所以我们可以优化空间为一维。而上一行以下标\(i\)结尾的最大值我们可以用\(maxx[i]\)表示。其实与滚动数组很像,但是滚动数组只是优化了空间。

我们这个做法除了优化空间,还可以节省时间。因为如果没有\(maxx\)的预处理,我们根据上面第二个步骤计算上一排的最大值时需要循环遍历,浪费时间。这样预处理,节省空间和时间,一举两得。

时间复杂度为\(O(Tnm)\)

*注意:可能需要输入优化(数据量比较大,题目中也有提到)

Code

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int m,n,a[1000010],dp[1000010],maxx[1000010];
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	while(cin>>m>>n){
		for(int i=1;i<=n;i++){
			cin>>a[i];
		}
		memset(dp,0,sizeof dp);
		memset(maxx,0,sizeof maxx);
		for(int i=1;i<=m;i++){
			for(int j=i;j<=n;j++){
				dp[j]=a[j]+max(dp[j-1],maxx[j-1]);
			}
			for(int j=i;j<=n;j++){
				maxx[j]=max(maxx[j-1],dp[j]);
			}
		}
		int ans=INT_MIN;
		for(int j=m;j<=n;j++) ans=max(ans,dp[j]);
		cout<<ans<<endl;
	}
	return 0;
}
posted @ 2024-03-24 21:35  Sinktank  阅读(21)  评论(0编辑  收藏  举报
★CLICK FOR MORE INFO★ TOP-BOTTOM-THEME
Copyright © 2023 ~ 2024 Sinktank - 1328312655@qq.com
Illustration from 稲葉曇『リレイアウター/Relayouter/中继输出者』,by ぬくぬくにぎりめし.