青科大编程赛热身赛D题:水晶双塔

题目概述:

	Mr. P有N块水晶,每块水晶有一个高度,
他想用这N块水晶搭建两座有同样高度的塔,使他们成为一座双塔,Mr. F可以从这N块水晶中任取M
(1≤M≤N)块来搭建。但是他不知道能否使两座塔有同样的高度,也不知道如果能搭建成一座双塔,这座
双塔的最大高度是多少。所以他来请你帮忙。
  给定水晶的数量N(1≤N≤100)和每块水晶的高度Hi(N块水晶高度的总和不超过2000),你的任务是
判断Mr. F能否用这些水晶搭建成一座双塔(两座塔有同样的高度),如果能,则输出所能搭建的双塔的最
大高度,否则输出“Impossible”。

输入格式:

 	 输入的第一行为一个数T,表示有T组测试数据。
	之后每组第一行为数N,表示水晶的数量。第二行为N个数,第i个数表示第i个水晶的高度。

输出格式:

	输出仅包含一行,如果能搭成一座双塔,则输出双塔的最大高度,否则输出一个字符串“Impossible”。

测试数据:

输入:
5
1 3 4 5 2
输出:
7

解题报告:

热身时这道题没有解出,虽然解出的人也不是很多……不过还是感觉特别丢人,毕竟同一个分组的都是大牛,我们差这一个,说不过去。

一开始想用贪心算法解,但是贪心法有个问题,贪心法做的是查找能达到的离总合一半高度的塔,注意这只是一个,而我们没有检验另一个塔能否达到这个高度。

事实证明这道题就是需要DP解法来做,回宿舍翻了一下别人的Blog对了一下思路,得出了DP方程和代码,过程如下:

设f[i,j]表示前i块水晶,两塔高度差为j时较高的那个塔的高度。

DP方程:

f[i,j]=max{f[i-1,j],f[i-1,j+h[i]],f[i-1,h[i]-j]+j}    (j

f[i,j]=max{f[i-1,j],f[i-1,j+h[i]],f[i-1,j-h[i]]+h[i]} (j>=h[i])

解析:

f[i-1,j]  表示不放第i块水晶;

f[i-1,j+h[i]] 表示第i块水晶放到了较矮的那个塔上,而且矮塔仍然为矮塔,填补了高度差;

f[i-1,h[i]-j]+j 表示第i块水晶放到了较矮的那个塔上,但是矮塔变成了高塔;

f[i-1,j-h[i]]+h[i]  表示第i块水晶放到了较高的塔上,增加了高度差;

边界条件:

如果刚开始的f值都为0的话,调试的时候会发现,有许多不合逻辑的答案,例如:h[1]=1,f[1,2]也会算出值为1,但其实是无法用高度为1的水晶搭出高度差大于等于2的双塔的。

所以要把所先把所有的f赋成-maxlongint,然后f[0,0]:=0。

代码如下:

#include
int main()
{
	int f[101][2001];
	int n,sum,i,j,k,t;
	int h[101];
	int max(int x,int y,int z);
	scanf("%d",&t);
	for(int o=1;o<=t;o++)
	{
		sum=0;
		scanf("%d",&n);
		for(int q=1;q<=n;q++)
		{
			scanf("%d",&h[q]);
			sum+=h[q];
		}
		f[0][0]=0;
		for(i=1;i<=n;i++)
		{
			for(j=0;j<=sum;j++)
			{
				if(h[i]0)
		printf("%d\n",f[n][0]);
		else
		printf("Impossible");
	}
}
int max(int x,int y,int z)
{
	int m=x;
	if(m

 ╮(╯_╰)╭ 
祝我明天的比赛能砍掉所有水题吧……阿门

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