1.题目描述:点击打开链接
2.解题思路:本题要求重涂尽量少的一些面,使得n个立方体完全相同。看上去很棘手的一个问题。第一种想到的方法就是暴力搜索。但首先要弄明白的一个问题是:一个正方体有几种不同的旋转方式?如果我们选择一个面作为顶面,有6种选法;接下来选一个面作为前面,有4种选法,此时该正方体的“姿态”就确定了,根据乘法原理,一共有6*4=24种姿态(这里用姿态代指旋转方式,一种姿态就是一种旋转方式)。
好了,接下来该怎么生成这24种姿态呢?我们事先定义一种“标准姿态”:编号为i的面位于位置i的立方体(题目中每个面的编号是1~6,为了便于处理,在程序中编号范围改为0~5,标准姿态的放置方式就是题目中的样子)。那么其他的23种姿态都可以通过这种标准姿态旋转得到。比如将“标准姿态”以3-4为轴,向左旋转,那么1->5,5->6,6->,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; }
原文地址:http://blog.csdn.net/u014800748/article/details/44223663