UVa-3401 Colored Cubes

题目概述:

有n个带颜色的立方体,每个面都涂有一种颜色。要求重新涂尽量少的面,使得所有立方体完全相同。两个立方体相同的含义是:存在一种旋转方式,是的两个立方体对应面的颜色相同。

思路:

每一个立方体有6个面,记为0,1,2,3,4,5.然后0面在上时,再讨论四个面哪个在前的情况。一共有6个面可以在上,每一个里面有四个面在前的情况。所以每个立方体都有24种姿态。定一动多。把一个立方体定住不动,作为参考系,然后再枚举剩下的立方体的姿态。在每一种确定的姿态里,比较每个面的颜色。以最多的颜色为准色,不一样的全都涂成准色。然后每一种确定的姿态都会算出一个结果,再取最小值即可。

注:24种姿态可事先通过程序枚举出来。注意!题目中每个面的编号是固定的!不可修改。

枚举姿态代码:

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

void trans(int *std,int *p)                    //std数组是用作变换的。p数组是现在的面的情况。
{
	int q[6];
	for (int i=0;i<6;i++) q[i]=p[i];       //因为p会改变,所以先把p的值赋给一个不动的数组q,代为p用。
	for (int i=0;i<6;i++)
	{
		p[i]=q[std[i]];                //现在第i个面(p[i])的位置应该是变换前状态第std[i]面的编号。【注意理解!】
	}
}

int main()
{
	int up[6]={3,1,0,5,4,2};
	int left[6]={1,5,2,3,0,4};             //left和up数组实际上是关系数组。画画图可知,初始状态为0,1,2,3,4,5.上翻(up)后,第0面的位置变成了刚刚的3面,第1面的位置不变,第2面的位置变成了刚刚的0面……以此类推。实际是表示的面和面的关系。
	int stds[6]={0,1,2,3,4,5};
	for (int k=0;k<6;k++)
	{
		int s[6];
		for (int i=0;i<6;i++) s[i]=stds[i];
		if (k == 1)
		{
			trans(up,s);
		}
		if (k == 2)
		{
			trans(left,s);
			trans(up,s);
		}
		if (k == 3)
		{
			trans(left,s); trans(left,s); trans(up,s);
		}
		if (k == 4)
		{
			trans(left,s); trans(left,s); trans(left,s); trans(up,s);
		}
		if (k == 5)
		{
			trans(up,s); trans(up,s);
		}
		for (int i=0;i<4;i++)
		{
			printf("{%d, %d, %d, %d, %d ,%d}",s[0],s[1],s[2],s[3],s[4],s[5]);
			trans(left,s);
		}
		cout<<endl;
	}
	return 0;
}

运行结果为:

{0, 1, 2, 3, 4 ,5},{1, 5, 2, 3, 0 ,4},{5, 4, 2, 3, 1 ,0},{4, 0, 2, 3, 5 ,1},
{3, 1, 0, 5, 4 ,2},{1, 2, 0, 5, 3 ,4},{2, 4, 0, 5, 1 ,3},{4, 3, 0, 5, 2 ,1},
{3, 5, 1, 4, 0 ,2},{5, 2, 1, 4, 3 ,0},{2, 0, 1, 4, 5 ,3},{0, 3, 1, 4, 2 ,5},
{3, 4, 5, 0, 1 ,2},{4, 2, 5, 0, 3 ,1},{2, 1, 5, 0, 4 ,3},{1, 3, 5, 0, 2 ,4},
{3, 0, 4, 1, 5 ,2},{0, 2, 4, 1, 3 ,5},{2, 5, 4, 1, 0 ,3},{5, 3, 4, 1, 2 ,0},
{5, 1, 3, 2, 4 ,0},{1, 0, 3, 2, 5 ,4},{0, 4, 3, 2, 1 ,5},{4, 5, 3, 2, 0 ,1}

然后提交程序代码:

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

int meiju[24][6]={								//枚举姿态数组
{0, 1, 2, 3, 4 ,5},{1, 5, 2, 3, 0 ,4},{5, 4, 2, 3, 1 ,0},{4, 0, 2, 3, 5 ,1},
{3, 1, 0, 5, 4 ,2},{1, 2, 0, 5, 3 ,4},{2, 4, 0, 5, 1 ,3},{4, 3, 0, 5, 2 ,1},
{3, 5, 1, 4, 0 ,2},{5, 2, 1, 4, 3 ,0},{2, 0, 1, 4, 5 ,3},{0, 3, 1, 4, 2 ,5},
{3, 4, 5, 0, 1 ,2},{4, 2, 5, 0, 3 ,1},{2, 1, 5, 0, 4 ,3},{1, 3, 5, 0, 2 ,4},
{3, 0, 4, 1, 5 ,2},{0, 2, 4, 1, 3 ,5},{2, 5, 4, 1, 0 ,3},{5, 3, 4, 1, 2 ,0},
{5, 1, 3, 2, 4 ,0},{1, 0, 3, 2, 5 ,4},{0, 4, 3, 2, 1 ,5},{4, 5, 3, 2, 0 ,1}
};

int n,ans;
int precolor[4][6];
int color[4][6];
int r[4];

vector<string> names;
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;
}

void check()								//运行到这时候,表示n个立方体的姿态已经确定。调用check函数检查需要涂色的最少数量。
{
	for (int i=0;i<n;i++)
		for (int j=0;j<6;j++)
			color[i][meiju[r[i]][j]]=precolor[i][j];	//color数组第i个立方体在第r[i]种姿态下第j个面的颜色。【注意理解!】
	
	int tot=0;
	for (int j=0;j<6;j++)
	{
		int color_appear_num[24];				//记录第i种颜色出现的次数
		memset(color_appear_num,0,sizeof(color_appear_num));
		int max_color_num=0;					//记录颜色出现的最多次数
		for (int i=0;i<n;i++)
		{
			max_color_num=max(max_color_num,++color_appear_num[color[i][j]]);
		}
		tot=tot+n-max_color_num;				//tot加上这一个面的涂色次数。涂色次数等于面数减去最多颜色出现次数。
	}
	ans=min(ans,tot);						//答案取最小值。
}

void dfs(int x)							//x表示正在确定第x个立方体的姿态。
{
	if (x == n) check();					//x==n表示n个立方体的姿态已经确定。
	else
		for (int i=0;i<24;i++)				//枚举第x个立方体的姿态。
		{
			r[x]=i;
			dfs(x+1);
		}
}
int main()
{
	while (scanf("%d",&n) == 1 && n)
	{
		names.clear();       				//使用vector实现字符串表示的颜色名到以数字代为表示的过程。实际上就是给颜色编号。
		for (int i=0;i<n;i++)				
			for (int j=0;j<6;j++)
			{
				char name[30];
				scanf("%s",name);
				precolor[i][j]=ID(name);	//precolor记录初始颜色。
			}
		ans=n*6;					//颜色最多有n*6种,所以涂色的上界就是n*6
		r[0]=0;				//r数组用来记录姿态。i表示第i个立方体。r[i]表示姿态。0表示不动。以第一个立方体为参考系,所以r[0]=0。
		dfs(1);						//使用深搜来实现枚举。
		cout<<ans<<endl;
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值