首页 > 代码库 > [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)