【题解】 P1651 塔

解法一:

f[i][j][k] = 0/1表示使用前i个水晶,第一个塔高j,第二个塔高k,是否可行。
决策:第i个水晶放不放,放在哪。
f[i][j][k] = f[i - 1][j][k] || f[i - 1][j - a[i]][k] || f[i - 1][j][k - a[i]]

解法二:

f[i][j] 表示从前i个积木中选,两组高度差为j, 高度较小的那一个塔高度最大是多少。

当两组双塔的高度差一样时,我们只将更高的那一组存入状态而舍弃更矮的那一组。如前三个积木3,5,7,两组状态的塔分别为:3,5和5,7时,我们只存f[3][2] = 5,对以后的状态转移无错误影响。

决策:

第i个水晶放不放,放在哪,放完之后两塔的高低关系是否发生改变。

转移(注意逆向思维):

第i个水晶不放,f[i][j] = f[i - 1][j]
第i个水晶放上之后是矮塔,之前肯定也是矮塔,之前高度差更大,f[i][j] = f[i - 1][j + h[i]] + h[i]
第i个水晶放上之后是高塔,之前也是高塔,之前高度差要小一些,矮塔高度不变,f[i][j] = f[i - 1][j - h[i]]
第i个水晶放上之后是高塔,之前是矮塔,原高塔是现矮塔,原高塔高度为原矮塔高度加高度差,f[i][j] =f[i - 1][h[i] - j] + h[i] - j

方程:

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

code

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdlib>
using namespace std;
const int maxn = 55, maxh = 500010;
int h[maxn], n, ans;
int sum, f[maxn][maxh], now;
int main(){
    scanf("%d", &n);
    memset(f, -1, sizeof(f));
    cin >> h[1];
    f[1][0] = 0;
    f[1][h[1]] = 0;
    now = h[1];
    for(int i = 2; i <= n; i++){
    	scanf("%d", &h[i]);
    	now += h[i];
    	for(int j = 0; j <= now; j++){
    		f[i][j] = f[i - 1][j];
//    		cout << j << " ";
    		if(f[i - 1][j + h[i]] != -1 && f[i - 1][j + h[i]] + h[i] <= now / 2){
//    			cout << "first " ;
	    		f[i][j] = max(f[i][j], f[i - 1][j + h[i]] + h[i]);// 矮-矮
	    	}
			if(j >= h[i] && f[i - 1][j - h[i]] != -1 && f[i - 1][j - h[i]] <= now / 2){
//				cout << "second " ;
				f[i][j] = max(f[i][j], f[i - 1][j - h[i]]);//高-高 
			}
			if(h[i] - j > 0 && f[i - 1][h[i] - j] != -1 && f[i - 1][h[i] - j] + h[i] - j){
//				cout << "third ";
				f[i][j] = max(f[i][j], f[i - 1][h[i] - j] + h[i] - j);//高-矮 
			}
//			cout << f[i][j] << endl;
		}
	}

	ans = max(ans, f[n][0]);

	if(ans != 0)	cout << ans << endl;
	else
		cout << -1 << endl;
	return 0;
}
posted @ 2020-09-04 17:55  _Buffett  阅读(136)  评论(0编辑  收藏  举报