UVA437「The Tower of Babylon」 1. 題目 2. 題解

1. 題目

題目鏈接:UVA437「The Tower of Babylon」

Description

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 (x_i, y_i, z_i). 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.
  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.

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 x_i, y_i and z_i.
  Input is terminated by a value of zero (0) for n.

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

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

2. 題解

分析

顯然這是一道 dp 題,因爲每個塊可以以給出的三條棱中的任意一條作爲高,我們不妨將一個塊按照高度取不同的棱分爲三個塊(即將原來的無向塊變成有向塊)。其次我們注意到,雖然每種有向塊都有無窮多個,但是其實一種有向塊最多隻有一個會出現在塔中(因爲要求塊底部長寬從下到上嚴格遞增),這樣問題就等價於給出 3n 個有向塊,求塔的最大高度。如何構造狀態以及狀態轉移方程是重點。定義有向塊數組 rect,其字段 l,w,h 分別表示有向塊的長、寬、高。這裏我們考慮順序遍歷所有有向塊進行動態規劃,則需要將一個有向塊的長、寬分別定義爲有向塊底面 l,w 中的較大值和較小值,然後根據先長後寬由大到小對有向塊進行排序,這樣才能保證最終塔高的最大值一定在順序遍歷有向塊過程中出現(因爲最終塔一定滿足從底塊到頂塊按長、寬嚴格遞減的條件,而滿足這種條件的所有可能塔的構造都在順序遍歷有向塊時出現)。構造的狀態及狀態轉移方程如下:

  • 首先構造狀態 dp[i][j] 表示遍歷完第 i 個塊後,塔頂爲第 j 個塊時塔還能獲得的最大高度。

  • 然後構造狀態轉移方程 dp[i][j] = \begin{cases} max(dp[i+1][j], dp[i+1][i+1]+rect[i+1].h) & if \ \ rect[j].l > rect[i+1].l \ \&\& \ rect[j].w > rect[i+1].w \\ dp[i+1][j] & others \end{cases}

最終答案即爲 dp[0][0]

代碼

#include <bits/stdc++.h>
#define ll int
#define MAXN 105
#define INF 0x3f3f3f3f
using namespace std;

// 有向塊
ll cnt;
struct Rect {
    ll l, w, h;
    bool operator < (const Rect r) const {
        return l == r.l ? w > r.w : l > r.l;
    }
}rect[MAXN];
// 將無向塊轉爲三個有向塊
// 並設定塊長度大於等於寬度
void addRect(ll a, ll b, ll c) {
    ++cnt, rect[cnt].l = max(a,b), rect[cnt].w = min(a,b), rect[cnt].h = c;
    ++cnt, rect[cnt].l = max(b,c), rect[cnt].w = min(b,c), rect[cnt].h = a;
    ++cnt, rect[cnt].l = max(c,a), rect[cnt].w = min(c,a), rect[cnt].h = b;
}

int main()
{
    ll n;
    ll t = 1;
    while(true) {
        scanf("%d", &n);
        if(n == 0)  break;
        cnt = 0;
        for(ll i = 0; i < n; ++i) {
            ll a, b, c;
            scanf("%d%d%d", &a, &b, &c);
            addRect(a, b, c);
        }
        // 對有向塊排序
        sort(rect+1, rect+cnt+1);
        // 初始第 0 塊爲底面
        rect[0].l = INF, rect[0].w = INF, rect[0].h = INF;
        // 假定最後一塊爲一個點
        ++cnt, rect[cnt].l = 0, rect[cnt].w = 0, rect[cnt].h = 0;
        ll dp[MAXN][MAXN] = {0};
        for(ll i = cnt-1; ~i; --i) {
            for(ll j = i; ~j; --j) {
                dp[i][j] = dp[i+1][j];
                if(rect[j].l > rect[i+1].l && rect[j].w > rect[i+1].w) {
                    dp[i][j] = max(dp[i][j], dp[i+1][i+1]+rect[i+1].h);
                }
            }
        }
        printf("Case %d: maximum height = %d\n", t++, dp[0][0]);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章