题目:uva437 - The Tower of Babylon(DAG上的DP)
题目大意:给你一些立方体,给出长宽高XYZ。现在希望你将这些立方题叠起来,使得最后的高度最大,并且这些立方体是可以无限次使用的,但是一个立方体要在另一个立方体的上面的话是需要满足这个立方体的底面是可以完全包含在下面的那个立方体的底面。
解题思路:其实这里的无限次使用没有什么用,因为一个立方体最多使用三次就不可能是再用。输入的一个立方体其实可以变成三个确定长宽高的立体。然后将这些立方体先做预处理,如果立方体j能够放在立方体i上面,那么i到j有一条有向边。这样只要在这个有向无环图里面搜索就可以。dp【i】【j】代表第i个立方体到第j个立方体可以(可以的话是i在下j在上)得到的最大的高度。dp【i】【j】 = Max(dp【j】【k】+ v【i】) k >= 1 && k <= 3 * n && G[j][k] = 1 初值dp【j】【k】 = v【j】;这里用记忆化搜索,觉得递推的顺序不太清晰。
代码:
#include <cstdio>
#include <cstring>
const int N = 95;
typedef long long ll;
int n;
int G[N][N];
ll dp[N][N];
struct BLOCK {
int x, y, z;
}b[N];
bool judge (int i, int j) {
if (b[i].x > b[j].x && b[i].y > b[j].y)
return true;
if (b[i].y > b[j].x && b[i].x > b[j].y)
return true;
return false;
}
void handle () {
memset (G, 0, sizeof (G));
for (int i = 0; i < 3 * n; i++)
for (int j = 0; j < 3 * n; j++) {
if (i != j && judge(i, j))
G[i][j] = 1;
}
}
ll Max (const ll a, const ll b) { return a > b? a: b; }
int DP (int x1, int y1) {
ll& ans = dp[x1][y1];
if (ans != -1)
return ans;
for (int i = 0; i < 3 * n; i++) {
if (y1 == i)
continue;
if (G[y1][i])
ans = Max (ans, DP(y1, i) + b[x1].z);
}
if (ans == -1)
ans = b[y1].z + b[x1].z;
return ans;
}
int main () {
int x, y, z;
int cas = 0;
ll ans;
while (scanf ("%d", &n), n) {
for (int i = 0; i < 3 * n; i = i + 3) {
scanf ("%d%d%d", &b[i].x, &b[i].y, &b[i].z);
b[i + 1].z = b[i].x;
b[i + 1].y = b[i].y;
b[i + 1].x = b[i].z;
b[i + 2].z = b[i].y;
b[i + 2].x = b[i].x;
b[i + 2].y = b[i].z;
}
handle();
ans = 0;
memset (dp, -1, sizeof (dp));
for (int i = 0; i < 3 * n; i++)
for (int j = 0; j < 3 * n; j++)
if (G[i][j])
ans = Max (ans, DP(i, j));
printf ("Case %d: maximum height = %lld\n", ++cas, ans);
}
return 0;
}