1.题目描述:点击打开链接
2.解题思路:本题要求重涂尽量少的一些面,使得n个立方体完全相同。看上去很棘手的一个问题。第一种想到的方法就是暴力搜索。但首先要弄明白的一个问题是:一个正方体有几种不同的旋转方式?如果我们选择一个面作为顶面,有6种选法;接下来选一个面作为前面,有4种选法,此时该正方体的“姿态”就确定了,根据乘法原理,一共有6*4=24种姿态(这里用姿态代指旋转方式,一种姿态就是一种旋转方式)。
好了,接下来该怎么生成这24种姿态呢?我们事先定义一种“标准姿态”:编号为i的面位于位置i的立方体(题目中每个面的编号是1~6,为了便于处理,在程序中编号范围改为0~5,标准姿态的放置方式就是题目中的样子)。那么其他的23种姿态都可以通过这种标准姿态旋转得到。比如将“标准姿态”以3-4为轴,向左旋转,那么1->5,5->6,6->2,2->1。如果我们令p[i]表示编号为i的面旋转后所在的位置,那么上述标准姿态左转后可以表示为{5,1,3,4,6,2}。由此,枚举姿态的思路就确定了:首先枚举某个编号为顶面,然后看它有几种姿态。比如:编号1在顶面时:先将标准姿态向上翻一次,在向左旋转0~3次;编号2为顶面时:将标准姿态向左旋转1次,向上翻1次,然后向左旋转0~3次;以此类推便能得到这24种姿态。
为了便于调试,这里有一个技巧:先独立写一个生成这24种姿态的程序,把这24种姿态输出到文件中,然后拷贝到最终的程序中当做常量表。这里把它们都放在数组dice24[24][6]中,dice24[i][j]便表示了第i种姿态编号j的位置。
接下来该暴力搜索了,那么如何“暴力”呢?这里的方法是先枚举每个立方体的姿态(第一个当做参考系,不用旋转),然后对于这6个面,分别选一个出现次数最多的颜色作为“标准”,和它不同的颜色一律重涂。这样,根据乘法原理易知,最多只有24^3种情况。本题的细节过多,这里只叙述了大致的枚举思路,详细细节见代码注释部分。
3.代码:
(生成24种姿态的代码)
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<functional>
using namespace std;
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]];//相当于映射:i->p->T
}
void enumerate_permutation()
{
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);//分别枚举将i变成顶面的操作
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++)//侧面的4种情况
{
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()
{
freopen("t.txt", "w", stdout);
enumerate_permutation();
return 0;
}
(最终的程序)
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<functional>
using namespace std;
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 },
};//24种姿态的常量表,dice24[i][j]表示第i种旋转姿态编号为j的面所在的位置(注意区分“编号”和“位置”)
#define N 4
int n, dice[N][6], ans;//dice[i][j]表示第i个立方体,编号为j的面的颜色
vector<string>names;
int ID(const char*name)//给每个颜色分配一个ID
{
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;
}
int r[N], color[N][6];//r[i]表示第i个立方体的姿态编号,color[i][j]表示第i个立方体的位置为j的面的颜色(注意是“位置”为j,不是“编号”为j)
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];//编号为j的面在姿态i中对应的位置是dice24[i][j]
int tot = 0;//统计需要重新涂的面的总次数
for (int j = 0; j < 6; j++)//枚举每个面
{
int cnt[N * 6];//cnt[i]表示颜色i出现的次数
memset(cnt, 0, sizeof(cnt));
int maxface = 0;
for (int i = 0; i < n; i++)//枚举每个立方体的位置为j的面的颜色
maxface = max(maxface, ++cnt[color[i][j]]);//统计出现次数最多的颜色的次数
tot += n - maxface;//在位置均为j时,需要重新涂的次数为n-maxface
}
ans = min(ans, tot);
}
void dfs(int d)//利用dfs枚举每个立方体的姿态,最多只有24^3种情况
{
if (d == n)check();//枚举完毕时,开始检查有多少面需要重涂
else for (int i = 0; i < 24; i++)
{
r[d] = i;
dfs(d + 1);
}
}
int main()
{
//freopen("t.txt", "r", stdin);
while (~scanf("%d", &n) && n)
{
names.clear();
for (int i = 0; i < n;i++)
for (int j = 0; j < 6; j++)
{
char name[30];
cin >> name;
dice[i][j] = ID(name);
}
ans = n * 6;
r[0] = 0;
dfs(1);
printf("%d\n", ans);
}
return 0;
}