hdu 1024 Max Sum Plus Plus

题意

n个数取m个不重合的部分使和最大,求这个最大值。

思路

枚举比大小的思路肯定不可取,尝试动态规划。
动态规划最自然的想法是:求n个数取若干部分和的最大值,子问题就是对n-1个数取若干部分和的最大值。
设a[n]为n个数的序列,dpp[n][m]为n个数取m部分的最大值,可以有两种决策达到dpp[n][m]的状态:

  1. 第n个数构成第m部分
  2. 第n个数不构成第m部分

所以状态转移方程, 1 ≤ x ≤ n − m + 1 1\leq x\leq n-m+1 1xnm+1
d p p [ n ] [ m ] = m a x ( m a x ( d p p [ n − x ] [ m − 1 ] + ∑ i = n − x + 1 n a [ i ] ) , d p p [ n − 1 ] [ m ] ) dpp[n][m]=max(max(dpp[n-x][m-1]+\sum_{i=n-x+1}^n a[i]), dpp[n-1][m]) dpp[n][m]=max(max(dpp[nx][m1]+i=nx+1na[i]),dpp[n1][m])
对于这个方程我们考虑实现,n需要一层遍历,m需要一层遍历,x需要一层遍历,变成的 O ( n 3 ) O(n^3) O(n3)的算法,应该会超时。
尝试优化第一个决策引入的x,尝试消除x的影响。
设dp[n][m]为n个数取m部分的最大值,且第n个数构成第m部分,之所以这样设是因为我观察到 m a x ( d p p [ n − x ] [ m − 1 ] + ∑ i = n − x + 1 n a [ i ] ) max(dpp[n-x][m-1]+\sum_{i=n-x+1}^n a[i]) max(dpp[nx][m1]+i=nx+1na[i])中有这样(dp[n][m])的部分。(有点类似最长上升子序列的状态)
d p [ n ] [ m ] = m a x ( d p p [ n − x ] [ m − 1 ] + ∑ i = n − x + 1 n a [ i ] ) dp[n][m]=max(dpp[n-x][m-1]+\sum_{i=n-x+1}^n a[i]) dp[n][m]=max(dpp[nx][m1]+i=nx+1na[i])
= m a x ( d p p [ n − x ] [ m − 1 ] + ∑ i = n − x + 1 n − 1 a [ i ] ) + a [ n ] =max(dpp[n-x][m-1]+\sum_{i=n-x+1}^{n-1} a[i])+a[n] =max(dpp[nx][m1]+i=nx+1n1a[i])+a[n]
= m a x ( d p p [ n − 1 ] [ m − 1 ] , d p [ n − 1 ] [ m ] ) + a [ n ] =max(dpp[n-1][m-1], dp[n-1][m])+a[n] =max(dpp[n1][m1],dp[n1][m])+a[n]
这样消除了x的影响,变成了 O ( n 2 ) O(n^2) O(n2)的算法。回过头在看最后公式的两部分可以看成:

  • 第n个数独立构成第m部分的情况
  • 第n个数和前面的数一同构成第m部分的情况

同时 d p p [ n ] [ m ] = m a x ( d p [ n ] [ m ] , d p p [ n − 1 ] [ m ] ) dpp[n][m]=max(dp[n][m], dpp[n-1][m]) dpp[n][m]=max(dp[n][m],dpp[n1][m])
由于状态转移方程只涉及相邻项的关系,所以可以状态压缩。(注意顺序)
这题动态规划的边界条件是试出来的,详情看代码。
看到航电discuss的另一中写法,把分成m部分看成阶段。

代码

#include 
#include 
#include 
using namespace std;

typedef long long LL;
const LL INF = -1e18;
int s[1000010];
LL dp[1000010];
LL dpp[1000010];

int main(){
	int m, n;
	while(~scanf("%d%d", &m, &n)){
		dpp[0] = dp[0] = 0;
		for(int i = 1; i <= m; ++i){
			dp[i] = INF;
			dpp[i] = INF;
		}
		for(int i = 1; i <= n; ++i){
			scanf("%d", s + i);
			for(int j = min(i, m); j > 0; --j){
				dp[j] = max(dpp[j-1], dp[j]) + s[i];
				dpp[j] = max(dpp[j], dp[j]);
			}
		}
		printf("%I64d\n", dpp[m]);
	}
	return 0;
}

总结

  1. 第一次感觉数学是一个工具,并且是一个好工具。

你可能感兴趣的:(动态规划)