首页 > 代码库 > 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;
}