题干:
描述
2001年9月11日,一场突发的灾难将纽约世界贸易中心大厦夷为平地,Mr. F曾亲眼目睹了这次灾难。为了纪念“9?11”事件,Mr. F决定自己用水晶来搭建一座双塔。
Mr. F有N块水晶,每块水晶有一个高度,他想用这N块水晶搭建两座有同样高度的塔,使他们成为一座双塔,Mr. F可以从这N块水晶中任取M(1≤M≤N)块来搭建。但是他不知道能否使两座塔有同样的高度,也不知道如果能搭建成一座双塔,这座双塔的最大高度是多少。所以他来请你帮忙。
给定水晶的数量N(1≤N≤100)和每块水晶的高度Hi(N块水晶高度的总和不超过2000),你的任务是判断Mr. F能否用这些水晶搭建成一座双塔(两座塔有同样的高度),如果能,则输出所能搭建的双塔的最大高度,否则输出“Impossible”。
格式
输入格式
输入的第一行为一个数N,表示水晶的数量。第二行为N个数,第i个数表示第i个水晶的高度。
输出格式
输出仅包含一行,如果能搭成一座双塔,则输出双塔的最大高度,否则输出一个字符串“Impossible”。
样例1
样例输入1
5
1 3 4 5 2
Copy
样例输出1
7
Copy
来源
某校NOIP模拟题
解题报告:
非常巧妙的dp题,类似于背包,dp[i][j]表示前i块木块,高块和低块的差值为j时,最低的木块的最大高度。(其实维护的是最高的木块的最大高度也可以)
注意判断无解是<=0而不是<0。因为一直不选木块也可以成功转移到dp[n][0]只是值为0)。
AC代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define FF first
#define SS second
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
typedef pair<int,int> PII;
const int INF = 0x3f3f3f3f;
const int MAX = 100 + 5;
int n;
int dp[MAX][5555];
int a[MAX];
int main()
{cin>>n;memset(dp,-INF,sizeof dp);dp[0][0] = 0;for(int i = 1; i<=n; i++) cin>>a[i];for(int i = 1; i<=n; i++) {for(int j = 0; j<=2004; j++) { dp[i][j] = max(dp[i][j],dp[i-1][j]);//第i块砖不放if(j>=a[i])dp[i][j] = max(dp[i][j],dp[i-1][j-a[i]]);//放在高的上 dp[i][j] = max(dp[i][j],dp[i-1][j+a[i]]+a[i]);//放在矮的上且没超过高塔if(a[i]>=j)dp[i][j] = max(dp[i][j],dp[i-1][a[i]-j]+(a[i]-j));}}int ans = dp[n][0];if(ans > 0) printf("%d\n",ans);else printf("Impossible\n");return 0 ;
}
/*
2
1 3*/
如果要维护最高高度的话就这样转移: