吴昊品游戏核心算法 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 

posted on 2013-02-27 21:09  吴昊系列  阅读(364)  评论(0)    收藏  举报

导航