上一次紫芝详细地介绍了动态规划中的经典问题LIS,今天我们抽出一个类似思想的简单题目进行实践练习。
The Tower of Babylon(巴比伦塔)
Perhaps you have heard of the legend of the Tower of Babylon. Nowadays many details of this tale have been forgotten. So now, in line with the educational nature of this contest, we will tell you the whole story:
你可能对巴比伦的传说有所耳闻,但如今诸多细节早已随风而逝。现在为了与比赛的教育目的相一致,我们将还原整个故事:
The babylonians had n types of blocks, and an unlimited supply of blocks of each type. Each type-i block was a rectangular solid with linear dimensions (xi,yi,zi). A block could be reoriented so that any two of its three dimensions determined the dimensions of the base and the other dimension was the height.
巴比伦人有 n 种无限供给的砖块,每个砖块 i 是三边为 xi,yi,zi 的长方体,可以以任意两边构成底,第三边为高。
They wanted to construct the tallest tower possible by stacking blocks. The problem was that, in building a tower, one block could only be placed on top of another block as long as the two base dimensions of the upper block were both strictly smaller than the corresponding base dimensions of the lower block. This meant, for example, that blocks oriented to have equal-sized bases couldn’t be stacked.
欲砌砖以筑最高楼。以上一个砖的两底边均严格小于下一个砖的两底边为原则(相等不算数)。
Your job is to write a program that determines the height of the tallest tower the babylonians can build with a given set of blocks.
作为程序员的你来写个bug看看用他给的砖能堆多高。
Input
The input file will contain one or more test cases. The first line of each test case contains an integer n, representing the number of different blocks in the following data set. The maximum value for n is 30. Each of the next n lines contains three integers representing the values xi, yi and zi. Input is terminated by a value of zero (0) for n.
输入:多组数据。每组第一行给出种类数 n,最大30;接下来 n 行每行给出这种砖的长宽高。输入以n==0结束。
Output
For each test case, print one line containing the case number (they are numbered sequentially starting from 1) and the height of the tallest possible tower in the format ‘Case case: maximum height = height’
输出:对于每组数据输出最高塔的高度值,格式Case要求见样例。
Sample Input
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
Sample Output
Case 1: maximum height = 40
Case 2: maximum height = 21
Case 3: maximum height = 28
Case 4: maximum height = 342
首先建议自己思考、编程实现并提交~
其实跟LIS还是不完全一样的(废话),但个人觉得思想都是对于每个元素都查一遍他前头有几个符合条件的。
简单再现一下我个人的思考过程:
啊这是单减序列啊 ----> 如果就按他输入的顺序扫一遍,遇到“大小中”这样的顺序我这个“中”好像插不进去啊 ----> 那先排个序? ----> 怎么排啊?俩边?面积? ----> 诶按面积排会不会出乱子呢?最后要严格递减的,那排后面的双边都小,肯定比前面的面积小啊;不在这个序列里的人他站哪都无所谓吧…… ----> 开始编程
放出蒟蒻代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 #include <vector> 6 using namespace std; 7 8 struct block 9 { 10 int a; 11 int b; 12 int height; 13 block(int x, int y, int z){a = x; b = y; height = z;} 14 }; 15 16 struct cmp 17 { 18 bool operator() (const block& x, const block& y) 19 { 20 return x.a * x.b > y.a * y.b; 21 } 22 }; 23 24 bool yes(block x, block y) 25 { 26 return (x.a > y.a && x.b > y.b) || (x.a > y.b && x.b > y.a); 27 } 28 29 int main() 30 { 31 int n, kase = 0; 32 while (~scanf("%d", &n) && n) 33 { 34 vector <block> v; 35 for (int i = 0; i < n; i++) 36 { 37 int a, b, c; 38 scanf("%d%d%d", &a, &b, &c); 39 //每块都有三种用法 40 v.push_back(block(a,b,c)); 41 v.push_back(block(a,c,b)); 42 v.push_back(block(b,c,a)); 43 } 44 45 sort(v.begin(), v.end(), cmp()); 46 47 int ans = v[0].height; 48 int dp[100] = {0}; 49 for (int i = 0; i < v.size(); i++) 50 { 51 dp[i] = v[i].height;//这是不能省的一步 52 //万一前面没有一个可以匹配的,它本身高度就是本序列的一血 53 for (int j = 0; j < i; j++)//前面扫荡一遍 54 if (yes(v[j], v[i]))//双边严格小于 55 dp[i] = max(dp[i], dp[j] + v[i].height); 56 ans = max(ans, dp[i]); 57 } 58 59 printf("Case %d: maximum height = %d ", ++kase, ans); 60 } 61 }
刘汝佳大爷给的教程是用记忆化搜索做的,将此题归类为DAG上的最长路问题。我稍加注释,下面贴上代码大家一起观摩~
1 // UVa437 The Tower of Babylon 2 3 // Rujia Liu 4 5 // 算法:DAG上的最长路,状态为(idx, k),即当前顶面为立方体idx,其中第k条边(排序后)为高 6 7 #include<cstdio> 8 9 #include<cstring> 10 11 #include<algorithm> 12 13 using namespace std; 14 15 #define REP(i,n) for(int i = 0; i < (n); i++) 16 //此处把for循环宏定义了一下,下面就比较好写 17 18 const int maxn = 30 + 5; 19 20 int n, blocks[maxn][3], d[maxn][3]; 21 22 void get_dimensions(int* v, int b, int dim) { 23 24 int idx = 0; 25 26 REP(i,3) if(i != dim) v[idx++] = blocks[b][i]; 27 } 28 29 int dp(int i, int j) { 30 31 int& ans = d[i][j]; 32 //加了&以后下面对ans的读写修改就相当于对d[i][j]的修改 33 34 if(ans > 0) return ans; 35 36 ans = 0; 37 38 int v[2], v2[2]; 39 40 get_dimensions(v, i, j); 41 //这个j的用途就是决定一下哪个边作为高 42 43 REP(a,n) REP(b,3) { 44 45 get_dimensions(v2, a, b); 46 47 if(v2[0] < v[0] && v2[1] < v[1]) ans = max(ans, dp(a,b)); 48 //因为他的边存的时候就是排好序的,故不会纠结旋转90°会不会成立的问题 49 50 } 51 52 ans += blocks[i][j]; 53 54 return ans; 55 56 } 57 58 int main() { 59 60 int kase = 0; 61 62 while(scanf("%d", &n) == 1 && n) { 63 64 REP(i,n) { 65 66 REP(j,3) scanf("%d", &blocks[i][j]); 67 68 sort(blocks[i], blocks[i]+3); 69 //将三边由小到大排序 70 } 71 72 memset(d, 0, sizeof(d)); 73 //这个d就是个dp数组,d[i][j]表示: 74 //i种类的砖以第j条边做高,且这块砖作为塔顶时,能达到的最大高度 75 76 int ans = 0; 77 78 REP(i,n) REP(j,3) ans = max(ans, dp(i,j));//这里是个二重循环 79 80 printf("Case %d: maximum height = %d ", ++kase, ans); 81 82 } 83 84 return 0; 85 }