UVa 253 Cube painting
这道题酝酿三天了,O(∩_∩)O哈哈~,其实就是懒~~
而且是1A哦~
算是近期做的比较难的一道题了。
本题可以参考刘汝佳的《算法竞赛入门经典训练指南》里第一章例8(Colored Cubes, LA 3401),本题只是书中例题的简化版。
问题分析:怎么判断两个正方体是否相等呢?我是用一个结构体来存放CUBE的,其中有三个变量就记录了每个正方体每种颜色的面数。
举个例子,如果有个正方体有两个面试红色,而另一个没有红色面,那这两个肯定不等。
所以,两个正方体相等的必要条件就是每种颜色的面数都相等。根据这一条件即可否定一部分情况。
Cube painting |
We have a machine for painting cubes. It is supplied with threedifferent colors: blue, red and green. Each face of the cube gets one of thesecolors. The cube's faces are numbered as in Figure 1.
Figure 1.
Since a cube has 6 faces, our machine can paint a face-numberedcube in different ways.When ignoring the face-numbers, the number of different paintings is much less,because a cube can be rotated. See example below. We denote a painted cube by astring of 6 characters, where each character is a b, r, or g. The character ( ) from the leftgives the color of face i.For example, Figure 2 is a picture of rbgggr and Figure 3corresponds to rggbgr. Notice thatboth cubes are painted in the same way: by rotating it around the vertical axisby 90 , the onechanges into the other.
Input
The input of your program is a textfile that ends with thestandard end-of-file marker. Each line is a string of 12 characters. The first6 characters of this string are the representation of a painted cube, theremaining 6 characters give you the representation of another cube. Yourprogram determines whether these two cubes are painted in the same way, that is,whether by any combination of rotations one can be turned into the other.(Reflections are not allowed.)
Output
The output is a file of boolean. For each line of input, outputcontains TRUE if the secondhalf can be obtained from the first half by rotation as describes above,FALSE otherwise.
SampleInput
rbgggrrggbgr
rrrbbbrrbbbr
rbgrbgrrrrrg
SampleOutput
TRUE
FALSE
FALSE
好了,下面是核心的内容。
我们要通过计算来算出第一个正方体旋转后的每个面的颜色来看看是否和第二个正方体对应面颜色一样。
那么一个正方体有多少种摆放方式呢?
第一步,确定一个顶面;第二部确定朝前的那个面,
所以一共有6 * 4 = 24 种
用T[i]中元素表示i号颜色 由T[i]号面的颜色旋转而来。
这句话很重要,可以说是这个程序的核心思想。
比如说图中正方体从上往下看逆时针旋转90°,所得到的T[i]为{0, 2, 4, 1, 3, 5}(再次体会刚才那句)
那么如何把24个T[]都找出来,一种是手工给数组复制,但这样很费时且容易出错,不好调试。
另一种方法就是写个程序,让计算机自己全部找出来,生成一个常量表,然后放在代码里。
还是刚才那种想法,我们可以自己先把向左转和向上转对应的T找出来,分别是
{0, 2, 4, 1, 3, 5} 和 {1, 5, 2, 3, 0, 4}
然后每种姿态都可以通过这两种旋转方式组合来得到。
首先让每个面都作一次顶面,然后向左转四次。
下面是打表的代码:
1 #define LOCAL 2 #include <cstdio> 3 #include <cstring> 4 5 int left[] = {0, 2, 4, 1, 3, 5}; 6 int up[] = {1, 5, 2, 3, 0, 4}; 7 //p[i]表示编号i所在位置 8 void rot(int T[], int p[]) 9 { 10 int q[6]; 11 memcpy(q, p, sizeof(q)); 12 for(int i = 0; i < 6; ++i) 13 p[i] = T[q[i]]; 14 } 15 void enumerate_permutations() 16 { 17 freopen("253打表.txt", "w", stdout); 18 int p0[6] = {0, 1, 2, 3, 4, 5}; 19 printf("int dice[24][6] = {\n"); 20 for(int i = 0; i < 6; ++i) 21 { 22 int p[6]; 23 memcpy(p, p0, sizeof(p0)); 24 switch(i) 25 {<span style="white-space:pre"> //0好面本来就是顶面,不用变了 26 case 1:<span style="white-space:pre"> //1号面作为顶面 27 rot(up, p); 28 break; 29 case 2:<span style="white-space:pre"> //下面以此类推 30 rot(left, p); 31 rot(up, p); 32 break; 33 case 3: 34 rot(left, p); 35 rot(left, p); 36 rot(left, p); 37 rot(up, p); 38 break; 39 case 4: 40 rot(left, p); 41 rot(left, p); 42 rot(up, p); 43 break; 44 case 5: 45 rot(up, p); 46 rot(up, p); 47 break; 48 } 49 for(int j = 0; j < 4; ++j) 50 { 51 printf("{%d, %d, %d, %d, %d, %d},\n", p[0], p[1], p[2], p[3], p[4], p[5]); 52 rot(left, p); 53 } 54 } 55 printf("};\n"); 56 } 57 int main(void) 58 { 59 enumerate_permutations(); 60 return 0; 61 }
好了,有了这个表,我们就可以很方便的写AC代码了。
1 //#define LOCAL 2 #include <iostream> 3 #include <cstdio> 4 #include <cstring> 5 using namespace std; 6 7 struct CUBE 8 { 9 char color[6]; 10 int num_red; 11 int num_green; 12 int num_blue; 13 }cube1, cube2; 14 //正方体的24种姿态 15 int pos[24][6] = { 16 {0, 1, 2, 3, 4, 5}, 17 {0, 2, 4, 1, 3, 5}, 18 {0, 4, 3, 2, 1, 5}, 19 {0, 3, 1, 4, 2, 5}, 20 {1, 5, 2, 3, 0, 4}, 21 {2, 5, 4, 1, 0, 3}, 22 {4, 5, 3, 2, 0, 1}, 23 {3, 5, 1, 4, 0, 2}, 24 {1, 2, 0, 5, 3, 4}, 25 {2, 4, 0, 5, 1, 3}, 26 {4, 3, 0, 5, 2, 1}, 27 {3, 1, 0, 5, 4, 2}, 28 {1, 3, 5, 0, 2, 4}, 29 {2, 1, 5, 0, 4, 3}, 30 {4, 2, 5, 0, 3, 1}, 31 {3, 4, 5, 0, 1, 2}, 32 {1, 0, 3, 2, 5, 4}, 33 {2, 0, 1, 4, 5, 3}, 34 {4, 0, 2, 3, 5, 1}, 35 {3, 0, 4, 1, 5, 2}, 36 {5, 4, 2, 3, 1, 0}, 37 {5, 3, 4, 1, 2, 0}, 38 {5, 1, 3, 2, 4, 0}, 39 {5, 2, 1, 4, 3, 0}, 40 }; 41 42 void count(CUBE &cube); 43 bool judge(char p[], int T[], char q[]); 44 45 int main(void) 46 { 47 #ifdef LOCAL 48 freopen("253in.txt", "r", stdin); 49 #endif 50 51 char s[20]; 52 while(gets(s)) 53 { 54 int i; 55 for(i = 0; i < 6; ++i) 56 cube1.color[i] = s[i]; 57 for(; i < 12; ++i) 58 cube2.color[i - 6] = s[i]; 59 count(cube1); 60 count(cube2); 61 bool flag = false; 62 //两个正方体要能重合首先每种颜色的面数应当相等 63 if(cube1.num_red != cube2.num_red 64 || cube1.num_green != cube2.num_green 65 || cube1.num_blue != cube2.num_blue) 66 { 67 printf("FALSE\n"); 68 continue; 69 } 70 71 for(i = 0; i < 24; ++i) 72 { 73 flag = judge(cube1.color, pos[i], cube2.color); 74 if(flag) 75 break; 76 } 77 if(flag) 78 printf("TRUE\n"); 79 else 80 printf("FALSE\n"); 81 } 82 return 0; 83 } 84 //统计小正方体每种颜色的面数 85 void count(CUBE &cube) 86 { 87 cube.num_red = 0; 88 cube.num_green = 0; 89 cube.num_blue = 0; 90 for(int i = 0; i < 6; ++i) 91 { 92 if(cube.color[i] == 'r') 93 ++cube.num_red; 94 if(cube.color[i] == 'g') 95 ++cube.num_green; 96 if(cube.color[i] == 'b') 97 ++cube.num_blue; 98 } 99 } 100 //判断cube1按姿态T旋转能否和cube2重合 101 //T[i]中元素可以理解为,i号颜色 由T[i]号面的颜色旋转而来 102 bool judge(char p[], int T[], char q[]) 103 { 104 char p0[6]; 105 int i; 106 for(i = 0; i < 6; ++i) 107 p0[i] = p[T[i]];//这句应该就是整个程序最核心的代码了吧 108 //也很难理解 109 for(i = 0; i < 6; ++i) 110 if(p0[i] != q[i]) 111 return false; 112 return true; 113 }