UVA437 The Tower of Babylon

题意翻译

【题目】

你可能已经听说过巴比伦塔的传说。现在这个传说的许多细节已经被遗忘。所以本着本场比赛的教育性质,我们现在会告诉你整个传说:

巴比伦人有n种长方形方块,每种有无限个,第i种方块的三边边长是xi,yi,zi。对于每一个方块,你可以任意选择一面作为底,这样高就随着确定了。举个例子,同一种方块,可能其中一个是竖着放的,一个是侧着放的,一个是横着放的。

他们想要用堆方块的方式建尽可能高的塔。问题是,只有一个方块的底的两条边严格小于另一个方块的底的两条边,这个方块才能堆在另一个上面。这意味着,一个方块甚至不能堆在一个底的尺寸与它一样的方块的上面。

你的任务是编写一个程序,计算出这个塔可以建出的最高的高度。

【输入】

输入会包含至少一组数据,每组数据的第一行是一个整数n(n<=30),表示方块的种类数。 这组数据接下来的n行,每行有三个整数,表示xi,yi,zi。 输入数据会以0结束。

【输出】

对于每组数据,输出一行,其中包含组号(从1开始)和塔最高的高度。按以下格式: Case : maximum height = __

【输入样例】

1

10 20 30

2

6 8 10

5 5 5

7

1 1 1

2 2 2

3 3 3

4 4 4

5 5 5

6 6 6

7 7 7

5

31 41 59

26 53 58

97 93 23

84 62 64

33 83 27

0

【输出样例】

Case 1: maximum height = 40

Case 2: maximum height = 21

Case 3: maximum height = 28

Case 4: maximum height = 342

题解

详细可以查看lrj书。本题题解出自刘汝佳算法竞赛入门经典

在任何时候,只有顶面的尺寸会影响到后续决策,因此可以用二元组(a,b)来表示“顶面尺寸为 a * b”这个状态。因为每次增加个立方体以后顶面的长和宽都会严格减小,所以这个图是DAG,可以套用前面学过的DAG最长路算法。这个算法没问题,不过落实到程序上时会遇到一个问题:不能直接用d(a,b)表示状态值,因为a和b可能会很大。怎么办呢?可以用(idx, k)这个二元组来“间接”表达这个状态,其中idx为顶面立方体的序号,k是高的序号(假设输入时把每个立方体的3个维度从小到大排序,编号为0~2)。例如,若立方体3的大小为a * b * c(其中a≤b≤c),则状态(3,1)就是指这个立方体在顶面,且高是b(因此顶面大小为a * c)。因为idx是0~n-1的整数,k是0~2的整数,所以可以很方便地用二维数组来存取。状态总数是O(n)的,每个状态的决策有O(n)个,时间复杂度为O(n 2 )。

本题属于DAG的动态规划问题

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 35;
int d[maxn][3], blocks[maxn][3];
int n, kase;
void getDimensions(int *v, int idx, int k){
	int index = 0;
	for(int i = 0; i < 3; ++i) if(i != k){
		v[index++] = blocks[idx][i];
	}
}
int dp(int i, int j){
	int &ans = d[i][j];
	if(ans > 0) return ans;
	int v1[2], v2[2];
	getDimensions(v1, i, j);
	for(int a = 0; a < n; ++a)
		for(int b = 0; b < 3; ++b){
			getDimensions(v2, a, b);
			if(v2[0] < v1[0] && v2[1] < v1[1]) ans = max(ans, dp(a, b));
		}
	return ans += blocks[i][j];
}
int main(){
	freopen("data.in", "r", stdin);
	while(scanf("%d", &n) == 1 && n){
		memset(d, 0, sizeof(d));
		for(int i = 0; i < n; ++i){
			for(int j = 0; j < 3; ++j) 
				scanf("%d", &blocks[i][j]);
			sort(blocks[i], blocks[i] + 3);
		}
		int ans = 0;
		for(int i = 0; i < n; ++i)
			for(int j = 0; j < 3; ++j){
				ans = max(ans, dp(i, j));
			}
		printf("Case %d: maximum height = %d\n", ++kase, ans);
	}
}

不过一定要这样做吗?

显然还有其它做法,那么是什么呢?其实仔细看,会发现,其实这就是求最长递增子序列长度啊,只是这个字母带了权重。。。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1010;
int dp[maxn];
int n, m, kase;
struct data{
	int a, b, c;
	bool operator < (const data &rhs) const {
		return a * b < rhs.a * rhs.b;
	}
}blocks[maxn];
int main(){
	freopen("data.in", "r", stdin);
	while(scanf("%d", &n) == 1 && n){
		m = 0;
		for(int i = 0; i < n; ++i){
			int a, b, c;
			scanf("%d%d%d", &a, &b, &c);
			blocks[m].a = a; blocks[m].b = b; blocks[m++].c = c;
			blocks[m].a = a; blocks[m].b = c; blocks[m++].c = b;
			blocks[m].a = b; blocks[m].b = a; blocks[m++].c = c;
			blocks[m].a = b; blocks[m].b = c; blocks[m++].c = a;
			blocks[m].a = c; blocks[m].b = b; blocks[m++].c = a;
			blocks[m].a = c; blocks[m].b = a; blocks[m++].c = b;
		}
		memset(dp, 0, sizeof(dp));
		sort(blocks, blocks + m);
		int ans = 0;
		for(int i = 0; i < m; ++i){
			dp[i] = blocks[i].c;
			for(int j = 0; j < i; ++j){
				if(blocks[j].a < blocks[i].a && blocks[j].b < blocks[i].b){
					dp[i] = max(dp[i], dp[j] + blocks[i].c);
				}
			}
			ans = max(ans, dp[i]);
		}
		printf("Case %d: maximum height = %d\n", ++kase, ans);
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值