本题题目链接:http://poj.org/problem?id=1753
题目截图如下:
题目大意:在4*4棋盘上有黑白子若干,现需要翻动若干次棋盘上的棋子实现棋盘上的棋子全白或全黑。翻动的规则是:当翻动某一棋子时,其上下左右棋子也需被翻动。求翻动的最小次数,如果无法实现,则需要输出提示信息“Impossible”。
一道枚举的题目,根据分析,我们很容易知道同一个位置的棋子翻动两次与未翻动的状态相同,所以每个位置的棋子我们就可简化为翻动与不翻动两种状态。这样只需确定16个位置的翻动状况即可。同样的,如果最后翻动次数大于16我们可以判定为不存在一种翻动的可行方案。
先放上代码,之后会对递归过程进行详细讲解:
1 #include <stdio.h>
2 #include <iostream>
3 using namespace std;
4 int mem[16] = {0};//化二维为一维的数组
5 int ans = 100;//最后返回的回答
6 void change (int pos){
7 int x = pos/4;//判断边界条件
8 int y = pos%4;//判断边界条件
9 mem[pos] = !mem[pos];//先改变目标点
10 if (x>0)
11 mem[pos-4] = !mem[pos-4];
12 if (x<3)
13 mem[pos+4] = !mem[pos+4];
14 if (y>0)
15 mem[pos-1] = !mem[pos-1];
16 if (y<3)
17 mem[pos+1] = !mem[pos+1];
18 }//pos为要改变的点的坐标
19 bool Judge (){
20 for (int i = 0;i<15;i++){
21 if (mem[i] != mem[i+1])
22 return false;
23 }
24 return true;
25 }//判断是否实现预定目标
26 void Enum (int pos,int count){
27 if (Judge())
28 if (count < ans){
29 ans = count;
30 return ;
31 }
32 if (pos >= 16)
33 return ;
34 change (pos);
35 Enum (pos+1,count+1);
36 change (pos);
37 Enum (pos+1,count);
38 return ;
39 }
40 int main (int argc,char *argv[]){
41 char start;
42 for (int i=0;i<16;i++){
43 cin>>start;
44 if (start=='b'){
45 mem[i]=1;
46 }
47 }
48 Enum (0,0);
49 if (ans==100)
50 printf("Impossible");
51 else
52 printf("%d",ans);
53 return 0;
54 }
对递归部分的讲解:
1 change (pos);
2 Enum (pos+1,count+1);
3 change (pos);//复位操作
4 Enum (pos+1,count);
我对于这四条语句的理解是,我们把它理解为一种状态的转换:每一个位置有翻与不翻两种状态,其中1、2两句即表示该位置棋子翻转,并将该状态输入函数的过程。第三句实际上实现了一种“复位”操作,将原来翻转的棋子再翻转回来,再将该位置棋子未翻转的状态输入函数。或者我们更进一步理解,每个点的翻转与否构成了一个二叉树,我们找到最小值的过程就是对这个树进行深度遍历的过程。事实上,上述语句改编为以下形式亦可。
1 Enum (pos+1,count);
2 change (pos);
3 Enum (pos+1,count+1);
4 change (pos);//复位操作