输入描述:
输入包括两行: 第一行为整数n(1 ≤ n ≤ 50),即一共有n块砖块 第二行为n个整数,表示每一块砖块的高度height[i] (1 ≤ height[i] ≤ 500000)
输出描述:
如果小易能堆砌出两座高度相同的塔,输出最高能拼凑的高度,如果不能则输出-1. 保证答案不大于500000。
输入例子:
3 2 3 5
输出例子:
5
小易现在让能够堆砌出来的两座塔的高度尽量高,说明是求最优值。适合用DP,一块砖只能用一次就是01背包问题了。
接下来就是想怎么构造最优子结构 dp[i][j]
i肯定是前i个砖块,问题是怎么求当两堆砖块高度相等时,最高的情况。因为高度不等的两堆砖块加上一个砖块之后可能会相等,所以需要记录不相等的详细信息,那么j就是两堆砖块的高度差。在高度差为0的时候,就是两堆砖块高度相等的时候,那么此时两堆砖块任何一堆的高度就是我们相求的值。所以i表示前i个砖块,j表示两堆砖块的高度差,dp[i][j]表示其中一堆的高度
我们设两堆砖块分别为A堆,B堆。j=B堆高度-A堆高度。 dp[i][j]为B堆的高度。 (当然你也可以有其他的设置方法,只要符合红字就可以。)因为题目要求不能堆砌出相同高度时要输出-1,所以对于不可能的情况我们要将值设为-1表示,我们一开始假设所有的情况都不可能(除了dp[0][0] = 0,在0个砖块情况下,两堆砖块高度差为0,且B堆砖块高度为0,这是成立的)。
对于前i个砖块,要想求两堆高度差(B-A)为j时,B堆砖块高度的最大值。有以下几种情况,第一种,可能不使用第i个砖块,当前i-1个砖块,在两堆高度差为j时,B堆砖块高度已经是最大值,此时dp[i][j] = dp[i-1][j]。第二种,需要使用第i个砖块,且需要你把第i个砖块放到A堆时,此时高度差变为j,B堆砖块高度为最大值。设第i个砖块的高度为h[i],那么在前i-1个砖块的时候,高度差为j+h[i] (因为这里我们的高度差是B-A,你把砖块加到A堆时缩小了高度差), 此时因为第i个砖块放到A堆,B堆的高度不变,所以dp[i][j] = dp[i-1][j+h[i]]。第三种,需要使用第i个砖块,且把该砖块放到B堆上,此时高度差变为j,那么原来的高度差为j-hp[i],此时dp[i][j] = dp[i-1][j-h[i]] + h。 我们需要考虑以上三种可能情况,取最大值。
此时的代码如下:
int main()
{
int n = 0;
cin >> n;
vector<int> brick(n+1);
int sum = 0;
for (int i = 1; i <= n; ++i) {
cin >> brick[i];
sum += brick[i];
}
vector<vector<int>> dp(n+1, vector<int>(2 * sum + 1, -1));
dp[0][sum] = 0; //j表示B-A的长度,B-A可能为负,所以偏移sum个长度
for (int i = 1; i <= n; ++i) {
for (int j = 0; j <= 2 * sum; ++j) {
int h = brick[i];
dp[i][j] = dp[i - 1][j]; //如果j+h和j-h都不在数组边界内,那么直接使用上一次的结果
if (j + h <= 2 * sum && dp[i - 1][j + h] != -1) {
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j + h]); //新的砖头放在A,或者不用这个砖头
}
if (j - h >= 0 && dp[i-1][j - h] != -1) {
dp[i][j] = max(dp[i-1][j], dp[i-1][j - h] + h); //新的砖头放在B
}
}
}
if (dp[n][sum] == 0)
cout << -1 << endl;
else
cout << dp[n][sum] << endl;
return 0;
}
但使用的内存会超出限制,需要使用滚动数组。
因为我们只需要最后前i个砖块的信息,而不需要前i-1个砖块的信息,所以我们可以丢弃那些信息。
使用滚动数组后的代码。
int main()
{
int n = 0;
cin >> n;
vector<int> brick(n + 1);
int sum = 0;
for (int i = 1; i <= n; ++i) {
cin >> brick[i];
sum += brick[i];
}
int f = 1;
vector<vector<int>> dp(2, vector<int>(2 * sum + 1, -1));
dp[1-f][sum] = 0; //j表示B-A的长度,B-A可能为负,所以偏移sum个长度
for (int i = 1; i <= n; ++i) {
for (int j = 0; j <= 2 * sum; ++j) {
int h = brick[i];
dp[f][j] = dp[1-f][j]; //如果j+h和j-h都不在数组边界内,那么直接使用上一次的结果
if (j + h <= 2 * sum && dp[1-f][j + h] != -1) {
dp[f][j] = max(dp[f][j], dp[1-f][j + h] ); //新的砖头放在A,或者不用这个砖头
}
if (j - h >= 0 && dp[1-f][j - h] != -1) {
dp[f][j] = max(dp[f][j], dp[1 - f][j - h] + h); //新的砖头放在B
}
}
f = 1 - f;
}
if (dp[1-f][sum] == 0)
cout << -1 << endl;
else
cout << dp[1-f][sum] << endl;
return 0;
}