吴昊品游戏核心算法 Round 7 —— 熄灯游戏AI(有人性的Brute Force)(POJ 2811)

暴力分为两种,一种属于毫无人性的暴力,一种属于有人性 的暴力。前面一种就不说了,对于后面一种情况,我们可以只对其中的部分问题进行枚举,而通过这些子问题而推导到整个的问题中。我称之为有人性的Brute Force。这次的熄灯问题就采用了这一点,将原本时间复杂度为O(2^30)的问题降为O(2^6),虽然都是属于指数级别的问题,但是,由于巧妙地运 用了数据之间的规律,而将复杂程度大为降低。这样做避免了毫无人性的暴力,让计算机在解决问题的时候也轻松了许多。

  如图所示,这是一款在android手机上运行的Lights Off小游戏,我们可以看到,该游戏更换了一些皮肤,界面还是传统型的,游戏的关键在于将所有的灯都熄灭。

 

  如图,显示出了该游戏的一些规则:

 

  我们可以看到,如果一个一个实验的话,将会存在2^30次方的可能性。但是,我们很快就发现,数据之间是存在规律的,你如果可以确定第一行的全部状态,其 将直接地影响到第二行乃至第五行的状态。由于第五行是无法进行最后的调整的,所以,我们只需要判断第五行是否存在有完全熄灭的可能,这样,问题的复杂度就 降低到了2^6,就具备一定的可解性了!

  在给出有注释的源代码之前(此问题来自POJ),我还是说下亮点吧:

(1) 为什么我们之前要增加三个方向的哨兵位(0位)?是因为,这样的话,我们可以统一地调用这样的公式:press[r+1][c]=(puzzle[r] [c]+press[r][c]+press[r-1][c]+press[r][c-1]+press[r][c+1])%2;              

(2)在枚举函数enumate()中,我们可以将第一行的情况看作是从“000000”到“111111”的变化的六位数,每一次变化采用逐位增的原则,将这64种变化遍历完之后,我们便讨论了所有的情况,由此可以得出是否有解的结论了。

 

 1 #include<stdio.h>
 2  
 3   int puzzle[ 6][ 8],press[ 6][ 8];
 4  
 5   bool guess()
 6  {
 7     int c,r;
 8     // 首先调整第二行到第四行的等得亮灭情况,使其保持熄灭状态
 9      for(r= 1;r< 5;r++)
10    {
11       for(c= 1;c< 7;c++)
12      {
13        press[r+ 1][c]=(puzzle[r][c]+press[r][c]+press[r- 1][c]+press[r][c- 1]+press[r][c+ 1])% 2;                      
14      }                
15    }
16     // 最后我们来判断一下第五行的情况,这是我们唯一无法控制的,如果可以的话,我们就成功了
17      // 否则,我们需要改变第一行的状态,进一步来寻找
18      for(c= 1;c< 7;c++)
19    {
20       if((press[ 5][c- 1]+press[ 5][c]+press[ 5][c+ 1]+press[ 4][c])% 2!=puzzle[ 5][c])
21         return  false;                
22    }
23     return  true;     
24  }
25  
26   void enumate()
27  {
28     int c;
29     bool success;
30     for(c= 1;c< 7;c++)
31       // 开始时,对于第一行的所有列都不按下按钮,试探一下
32       press[ 1][c]= 0;     
33     while(guess()== false)
34    {
35       // 将第一行的所有2^6个状态一一遍历,这里还是缺乏容错性的,因为
36        // 如果一直没有解的话,最后会因为数组越界而报错
37       press[ 1][ 1]++;
38      c= 1;
39       while(press[ 1][c]> 1)
40      {
41        press[ 1][c]= 0;
42        c++;
43        press[ 1][c]++;                    
44      }                     
45    }
46     return;  
47  }
48  
49   int main()
50  {
51     int cases,i,r,c; // r,c代表行和列
52     scanf( " %d ",&cases);
53     // 在三个方向上增加哨兵位
54      for(r= 0;r< 6;r++)
55      press[r][ 0]=press[r][ 7]= 0;
56     for(c= 1;c< 7;c++)
57      press[ 0][c]= 0;
58     // 样例输入     
59      for(i= 0;i<cases;i++)
60    {
61       for(r= 1;r< 6;r++)
62      {
63         for(c= 1;c< 7;c++)
64        {
65          scanf( " %d ",&puzzle[r][c]);                
66        }                
67      }                    
68      enumate(); // 带搜索的枚举
69        // 样例输出
70       printf( " PUZZLE #%d\n ",i+ 1);
71       for(r= 1;r< 6;r++)
72      {
73         for(c= 1;c< 7;c++)
74        {
75          printf( " %d ",press[r][c]);                
76        }                
77        printf( " \n ");
78      }
79    }
80     return  0;
81  }
82  
83 

你可能感兴趣的:(round)