首页 > 代码库 > [ACM训练] 算法初级 之 基本算法 之 枚举(POJ 1753+2965)
[ACM训练] 算法初级 之 基本算法 之 枚举(POJ 1753+2965)
先列出题目:
1、POJ 1753
POJ 1753 Flip Game:http://poj.org/problem?id=1753
Sample Input
bwwb bbwb bwwb bwww
Sample Output
4
入手竟然没有思路,感觉有很多很多种情况需要考虑,也只能使用枚举方法才能解决了吧~
4x4的数组来进行数据存储的话操作起来肯定非常不方便,这里借用位压缩的方法来存储状态,使用移位来标识每一个位置的的上下左右的位置操作。 详细看这里。
1、当棋盘状态id为0(全白)或65535(全黑)时,游戏结束,0^1=1,1^1=0,所以翻转的操作可以通过异或操作来完成,而翻转的位置可以通过移位来确定。
2、结束标识!!!!!
分步骤:
1、从输入到位标识状态:
1 int state = 0; 2 char ch[4]; 3 4 while(cin>>ch) 5 { 6 for(int j = 0 ; j < 4 ; j++) 7 { 8 state = state<<1; 9 if(ch[j] == ‘b‘) 10 state += 1; 11 } 12 }
2、从一个状态到下一个状态的的转换,比如对应位置i处进行改变后得到的状态:
这里的16个数据使用代码生成可能是想不到的,但是可以通过手动变换一次获得~~~
1 int change[16] ={ 2 51200,58368,29184,12544, 3 35968,20032,10016,4880, 4 2248,1252,626,305, 5 140,78,39,19 6 }; 7 8 state = state^change[i];//对应第i个位置的改变 9 10 //上面的16个数据是由下面的代码得来的 11 12 int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}}; 13 void init() 14 { 15 int i,j,x,y,t,temp; 16 for(i=0;i<4;++i) 17 { 18 for(j=0;j<4;++j) 19 { 20 temp = 0; 21 temp ^= (1<<((3-i)*4+3-j)); //第一行代表16位的高4位,同理第一列也代表高位,所以棋盘(i,j)处在16位中的位置是((3-i)*4+3-j) 22 23 for(t=0;t<4;++t) 24 { 25 x = i + dir[t][0]; 26 y = j + dir[t][1]; 27 if(x<0 || y<0 || x>3 || y>3) 28 continue; 29 temp ^= (1<<((3-x)*4+3-y)); 30 } 31 cout<<temp<<" "; 32 } 33 cout<<endl; 34 } 35 }
3、判断是否满足情况只需要判断state == 0 || state == 65535 即可。
4、解决了小问题,再思考一下大逻辑:
初始状态即为纯色则直接输出0,初始不是纯色,翻转一个的状态是纯色,则输出1,翻转一个会产生n种状态,0<=n<=16,这些状态要放入到队列中进行保存,再从中出队列再进行下一次的翻转。关键问题是什么情况下判定翻转结束,仍然没有纯色出现,则输出impossible,大于2次的翻转的初态都是从队列中取出来的,必须提前设计一个状态标识,表明同一种状态不再次进行入队列,那么当队列为空时才可以下结论。
1 //需要设置一个是否处理过的标识,共有状态有65536个,即0-65535, 2 //如果对应位置被标记为1了,则说明已经处理过,直接抛弃进行下一次 3 4 //这样的遍历过程被称为BFS的过程 5 6 7 bool visit[65536]; 8 9 int bfs(int state)//返回值是进行的步数,impossible为-1 10 { 11 queue<Node> q; 12 Node current,next; 13 14 current.state = state; 15 current.step = 0; 16 q.push(current); 17 visited[state] = true; 18 19 while(!q.empty()) 20 { 21 current = q.front(); 22 q.pop(); 23 24 if(current.state == 0 || current.state == 65535) 25 return current.step; 26 27 for(int i = 0;i<16;i++)//每一种状态都要进行16次操作 28 { 29 next.state = current.state^change[i]; 30 next.step = current.step+1; 31 32 if(next.state == 0 || next.state == 65535) 33 return next.step; 34 else 35 { 36 if(visited[next.state]) 37 continue; 38 else 39 { 40 visited[next.state] = true; 41 q.push(next); 42 } 43 } 44 } 45 } 46 return -1; 47 }
总结:枚举,此题就是使用枚举的思想来找到需要的解,尤其是使用队列来进行一个的while循环来进行的。
下一个类似的题目是POJ的2965题目:The Pilots Brothers‘ refrigerator http://poj.org/problem?id=2965
Sample Input
-+-- ---- ---- -+--
Sample Output
6 1 1 1 3 1 4 4 1 4 3 4 4
[ACM训练] 算法初级 之 基本算法 之 枚举(POJ 1753+2965)