UVa1352彩色立方体(部分枚举和贪心)

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=456&page=show_problem&problem=4098

题意:n个带颜色立方体,每个面一种颜色,要求重新涂尽量少的面,使得所有立方体完全相同。

思路:我们给一个立方体的每个面标上号码0-5时,每个立方体有24种不同的状态,我们通过枚举立方体的24种状态,然后在六个面的每个面选择出现次数最多的颜色,其他不同的重涂(贪心的思想)。


24种状态的生成需要使用的一段代码,也可以手动写出来不过出错几率较大。

#include<cstdio>
#include<cstring>

int left[] = {4, 0, 2, 3, 5, 1};
int up[] = {2, 1, 5, 0, 4, 3};

void rot(int* T, int* p)
{
	int q[6];
	memcpy(q, p, sizeof(q));
	for (int i = 0; i < 6; i++) p[i] = T[q[i]];
}

void enumerate_permutations()
{
	int p0[6] = {0, 1, 2, 3, 4, 5};
	printf("int dice24[24][6] = {\n");
	for (int i = 0; i < 6; i++)
	{
		int p[6];
		memcpy(p, p0, sizeof(p0));
		if (i == 0) 
            rot(up, p);
		if (i == 1)
		{
			rot(left, p);
			rot(up, p);
		}
		if (i == 3)
		{
			rot(up, p);
			rot(up, p);
		}
		if (i == 4)
		{
			rot(left, p);
			rot(left, p);
			rot(left, p);
			rot(up, p);
		}
		if (i == 5)
		{
			rot(left, p);
			rot(left, p);
			rot(up, p);
		}
		for (int j = 0; j < 4; j++)
		{
			printf("{%d, %d, %d, %d, %d, %d},\n", p[0], p[1], p[2], p[3], p[4], p[5]);
			rot(left, p);
		}
	}
	printf("};\n");
}

int main()
{
	enumerate_permutations();
	return 0;
}

有了状态之后,就开始枚举贪心了,因为题目n是不大于4的,所以枚举是无压力的。

#include<cstdio>
#include<cstring>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;

const int dice24[24][6] =
{
	{2, 1, 5, 0, 4, 3}, {2, 0, 1, 4, 5, 3}, {2, 4, 0, 5, 1, 3}, {2, 5, 4, 1, 0, 3}, {4, 2, 5, 0, 3, 1},
	{5, 2, 1, 4, 3, 0}, {1, 2, 0, 5, 3, 4}, {0, 2, 4, 1, 3, 5}, {0, 1, 2, 3, 4, 5}, {4, 0, 2, 3, 5, 1},
	{5, 4, 2, 3, 1, 0}, {1, 5, 2, 3, 0, 4}, {5, 1, 3, 2, 4, 0}, {1, 0, 3, 2, 5, 4}, {0, 4, 3, 2, 1, 5},
	{4, 5, 3, 2, 0, 1}, {1, 3, 5, 0, 2, 4}, {0, 3, 1, 4, 2, 5}, {4, 3, 0, 5, 2, 1}, {5, 3, 4, 1, 2, 0},
	{3, 4, 5, 0, 1, 2}, {3, 5, 1, 4, 0, 2}, {3, 1, 0, 5, 4, 2}, {3, 0, 4, 1, 5, 2},
};//上面代码生成的状态

const int maxn = 4;

int n, dice[maxn][6], ans;
int r[maxn], color[maxn][6]; // 每个立方体的旋转方式和旋转后各个面的颜色
vector<string> names;

inline int ID(const char* name)
{
	string s(name); 
	int n = names.size();
	for (int i = 0; i < n; i++)
		if (names[i] == s)
			return i;
	names.push_back(s);
	return n;
}

inline void check()
{
	for (int i = 0; i < n; i++)
		for (int j = 0; j < 6; j++)
			color[i][dice24[r[i]][j]] = dice[i][j]; // 旋转后各个面的颜色
	int tot = 0; // 需要重新涂色的面数
	for (int j = 0; j < 6; j++) // 考虑每个面
	{
		int cnt[maxn * 6]; // 每种颜色出现的次数
		memset(cnt, 0, sizeof(cnt));
		int maxface = 0;
		for (int i = 0; i < n; i++)
			maxface = max(maxface, ++cnt[color[i][j]]);
		tot += n - maxface;//累加需要重涂的
	}
	ans = min(ans, tot);
}

void dfs(int d)
{
	if (d == n)
		check();
	else
	{
		for (int i = 0; i < 24; i++)
		{
			r[d] = i; // 立方体的旋转都试一遍
			dfs(d + 1);
		}
	}
}

int main(void)
{
	while (scanf("%d", &n), n)
	{
		names.clear();
		for (int i = 0; i < n; i++)
			for (int j = 0; j < 6; j++)
			{
				char name[30];
				scanf("%s", name);
				dice[i][j] = ID(name);
			}
		ans = n * 6; // 上界:所有面都重涂色
		r[0] = 0; // 第一个立方体不旋转
		dfs(1);
		printf("%d\n", ans);
	}
	return 0;
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值