首页 > 代码库 > UVA - 10051Tower of Cubes(递推)
UVA - 10051Tower of Cubes(递推)
题目: UVA - 10051Tower of Cubes(递推)
题目大意:给出N个正方体1-N,只有序号小的正方体可以放在序号大的正方体的上面,并且除了最底下的那个正方体,其他的正方体的底面要和它下面的正方体的上面颜色相同。问怎样组合才能使得用的正方体个数越多。并且输出其中的一种堆放方式。
解题思路:一开始觉得是用DAG上的DP来做,结果状态开太多dp【N】【N】【M】(N代表正方体个数, M代表六个面),最后输出路径的时候超时了。后面看了别人的题解,发现状态只需要开dp[N][M]就足够了,因为只要在意这个正方体是哪个面在上面,下面的正方体的范围是确定的,那么组成方式最优的肯定是确定的。不需要将下面接着哪个正方体也记录下来。
状态转移方程:dp【i】【k】 = (dp【i + n】【m】 + 1) m:和第i个正方体底面颜色相同的i + n个正方体的那个面。 i + n 代表i之后的正方体从哪个开始。意思就是从i之后的正方体任选一个作为i后面的那个正方体,然后在用这个i + n的正方题的m面朝上的最多的个数再加上1.
初始化的时候要小心,dp【i】【k】 = 1[(i >= 1 && i <= N) (k >= 0 && k < M)】只取一个的时候,任何一个正方体哪个面朝上的组合方式的最多的个数是1。
这题的路径打印也是很无语,还得先找到对的路径,然后用next【j】(j这个立方体后面接哪个立方体最优),和side【j】(j这个立方体哪个面朝上最优)记录下来,最后在打印出来。
代码:
#include <cstdio> #include <cstring> const int N = 505; const int M = 6; const char str[M][2 * M] = {"front", "left", "top", "bottom", "right", "back"}; int n; int cube[N][M]; int dp[N][M]; int next[N], side[N]; int Max (const int a, const int b) {return a > b? a: b;} bool printf_ans (int d, int k) { if (dp[d][k] == 1) { next[d] = d; return true; } for (int j = d + 1; j <= n; j++) for (int i = 0; i < M; i++) if (cube[d][M - 1 - k] == cube[j][i] && dp[d][k] == dp[j][i] + 1) { if (printf_ans(j, i)) { next[d] = j; side[j] = i; return true; } } return false; } int main () { int cas = 0; while (scanf ("%d", &n) , n) { if (cas) printf ("\n"); for (int i = 1; i <= n; i++) { for (int j = 0; j < M/2; j++) scanf ("%d%d", &cube[i][j], &cube[i][M - 1 - j]); } //init for (int j = 1; j <= n; j++) for (int i = 0; i < M; i++) dp[j][i] = 1; int ans = 1; int d, k; for (int i = n - 1; i >= 1; i--) for (int j = 0; j < M; j++) { for (int l = i + 1; l <= n; l++) { for (int k = 0; k < M; k++) if (cube[i][M - 1 - j] == cube[l][k]) { if (dp[l][k] + 1 > dp[i][j]) { dp[i][j] = Max (dp[i][j], dp[l][k] + 1); } } } if (dp[i][j] > ans) { d = i; k = j; ans = dp[i][j]; } } printf ("Case #%d\n%d\n", ++cas, ans); //path if (ans == 1) { printf ("1 front\n"); continue; } printf_ans (d, k); while (1) { printf ("%d %s\n", d, str[k]); if (d == next[d]) break; d = next[d]; k = side[d]; } } return 0; }