poj1753 flip game题解
题目描述: Flip game is played on a rectangular 4x4 field with two-sided pieces placed on each of its 16 squares. One side of each piece is white and the other one is black and each piece is lying either it's black or white side up. Each round you flip 3 to 5 pieces, thus changing the color of their upper side from black to white and vice versa. The pieces to be flipped are chosen every round according to the following rules:
Consider the following position as an example: bwbw wwww bbwb bwwb Here "b" denotes pieces lying their black side up and "w" denotes pieces lying their white side up. If we choose to flip the 1st piece from the 3rd row (this choice is shown at the picture), then the field will become: bwbw bwww wwwb wwwb The goal of the game is to flip either all pieces white side up or all pieces black side up. You are to write a program that will search for the minimum number of rounds needed to achieve this goal. Input The input consists of 4 lines with 4 characters "w" or "b" each that denote game field position.
Output Write to the output file a single integer number - the minimum number of rounds needed to achieve the goal of the game from the given position. If the goal is initially achieved, then write 0. If it's impossible to achieve the goal, then write the word "Impossible" (without quotes).
题目分析
棋盘上共有16颗棋子,每个棋子只有黑白两种状态,故我可以将其看成一个16为的二进制数,黑为1白为0,每次翻棋子生成的棋盘相当于跟对应的数做一次异或运算,最后用一个广搜将搞定。 下面是我的代码
#include<iostream> #include<cmath> using namespace std; int main() { int os=0;//orignal state int fs[131072];// fliped state:队列 // flip number const int fn[16]={ 51200,58368,29184,12544,35968,20032,10016,4880, 2248, 1252, 626, 305, 140, 78, 39, 19}; //棋板存在状态登记数组 int exist[65536]; memset(exist,0,sizeof(exist)); //接收输入并将其转化成整数 for(int i=0;i<16;i++) { char temp; cin>>temp; int base=(temp=='b' ? 1:0);//黑为1,白为0 os+=(int)base*pow((double)2,15-i); } fs[0]=os; int out=0; int in=1; int levelEnd=0; int levelCount=0; bool flag=true; while(flag && out<=levelEnd) { while(out<=levelEnd) { int chudui=fs[out]; if( chudui==65535 || chudui==0 ) { flag=false; break; } for(int i=0;i<16;i++) { int fliped=chudui^fn[i]; //如果不曾出现过 if(exist[fliped]==0) { fs[in]=fliped; exist[fliped]++; in++; } } out++; } levelCount++; levelEnd=in-1; } if(out>levelEnd) cout<<"Impossible"<<endl; else cout<<levelCount-1<<endl; return 0; }
其中需要注意的就是我的存取位置标志in,in表示我下次入队的位置,因为对头在while循环外已经入队 了,所以in应该从1开始,故每扫完一层,应将in-1赋给下一层的层尾levelEnd(开始我直接将in赋给 levelEnd,卡了我两天啊) 另外该方法的时间开销是32毫秒,相比在网上看到的一个很不好理解的方法效率要低一些下面是他的 代码 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 using namespace std; 6 char map[5][5]; 7 bool flip[7][7]; 8 int m=20; 9 void change(int a,int b,int c,int d,char color) 10 { 11 int i,j,count=a+b+c+d; 12 flip[1][1]=a; 13 flip[1][2]=b; 14 flip[1][3]=c; 15 flip[1][4]=d; 16 for(i=1;i<4;i++) 17 { 18 for(j=0;j<4;j++) 19 { 20 if(map[i-1][j]==color) 21 flip[i+1][j+1]=(flip[i][j+1]+flip[i][j]+flip[i][j+2]+flip[i-1][j+1])%2; 22 else 23 flip[i+1][j+1]=(flip[i][j+1]+flip[i][j]+flip[i][j+2]+flip[i-1][j+1]+1)%2; 24 count+=flip[i+1][j+1]; 25 } 26 } 27 for(j=0;j<4;j++) 28 { 29 int f=(map[3][j]==color) ? 1 : -1; 30 if(f==1 && (flip[4][j+1]+flip[3][j+1]+flip[4][j]+flip[4][j+2])%2!=0) 31 break; 32 if(f==-1 && (flip[4][j+1]+flip[3][j+1]+flip[4][j]+flip[4][j+2])%2==0) 33 break; 34 } 35 if(j==4 && count<m) 36 m=count; 37 } 38 int main () 39 { 40 int a,b,c,d; 41 for(int i=0;i<4;i++) 42 scanf("%s",map[i]); 43 for(i=1;i<=4;i++) 44 { 45 flip[0][i]=0;flip[i][0]=0;flip[i][5]=0;//flip[ ]数组边界置为0 46 } 47 for(a=0;a<2;a++) 48 for(b=0;b<2;b++) 49 for(c=0;c<2;c++) 50 for(d=0;d<2;d++) 51 { 52 change(a,b,c,d,'w'); 53 change(a,b,c,d,'b'); 54 } 55 if(m==20) 56 printf("Impossible\n"); 57 else 58 printf("%d\n",m); 59 return 0; 60 }
目前本人尚无法理解这个神奇的方法,等待大牛指点 |
Bingo,go,go,go!