vijos1037 搭建双塔-状态优化dp

传送门

题目大意:自行参考

题解:

这个题非常适合初学者思考!!!

建议不要看题解,先想一想基本方法,在一步步优化。

好我开始说。

我们先只考虑“能否达到”的问题(因为一开始我是从ppt上看的,ppt上没有说让你求最大高度)。

首先第一眼是不是01背包可行性问题?

然后发现这有个问题,就是你并不能保证每个物品最多只用来建了一座塔,想想为啥。

然后改成dp[n][m1][m2]表示两个塔分别高m1m2。

复杂度O(n*m^2)显然会TLE。

然后,显然算法瓶颈在于状态繁杂,所以考虑把m1m2合并。(因为n显然是合并不了的,而且m1m2又这么像)

然后就不会做了,我就想能不能从状态转移方程入手,结果突然想到,如果m1m2的差就是当前的hi那么不就好了么?!

换句话说我们并不关心他们具体的值而只是关心他们的差!

然后一个小小的处理就是m1-m2可能是个负数,但是显然有|m1-m2|<=sigma{hi}(记作M)

所以用dp[n][m]表示m1-m2+M=m的情况。

则m1=m2即m=M。

转移就是dp[n][m]等于dp[n-1][m-h[i]] | dp[n-1][m+h[i]] | dp[n-1][m]。

初始dp[0][m]=true,其它=false。

然后就去看题发现还要求高度。

那也很好办啊,让dp[n][m]表示当前情况下两塔的高度之和是多少(显然如果知道了两塔的高度之和差就可以确定两塔高度)。

并且用-1来表示不存在(也就是之前的false)

那么转移就再加一句if(from>=0)即可。由于是和,所以最后输出要/2。

就这。

代码:

//
#include
#include
#include
#define MAXN 110
#define MAXM 4100
using namespace std;
int dp[MAXN][MAXM],h[MAXN];
int main()
{
	int n,m=0;scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&h[i]);
		m+=h[i];
	}
	for(int i=0;i<=2*m;i++)
		dp[0][i]=-1;
	dp[0][m]=0;
	for(int i=1;i<=n;i++)
		for(int j=0;j<=2*m;j++)
		{
			dp[i][j]=-1;
			if(j-h[i]>=0&&dp[i-1][j-h[i]]>=0) dp[i][j]=max(dp[i][j],dp[i-1][j-h[i]]+h[i]);
			if(j+h[i]<=2*m&&dp[i-1][j+h[i]]>=0) dp[i][j]=max(dp[i][j],dp[i-1][j+h[i]]+h[i]);
			if(dp[i-1][j]>=0) dp[i][j]=max(dp[i][j],dp[i-1][j]);
		}
//	for(int i=1;i<=n;i++,printf("\n"))
//		for(int j=0;j<=2*m;j++)
//			printf("%d ",dp[i][j]);
	if(dp[n][m]>0) printf("%d\n",dp[n][m]/2);
	else printf("Impossible\n");
}

你可能感兴趣的:(DP动态规划,状态优化dp)