例题1.8 彩色立方体 UVa1352

来源:互联网 发布:简单游戏编程 编辑:程序博客网 时间:2024/05/01 19:46

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 4int 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;}


0 0
原创粉丝点击