POJ 1753 Flip Game

 零、首先数据n=16,完全可以暴搜。

      2^20=1048576,一秒千万次操作,n<=20的数据都可以暴搜。

  仔细想一下,现在用的是枚举不是暴搜……

 

一、其实思路不难:

1.每一个位置只有翻奇数次和翻偶数次两种情况,而且偶数次相当于不翻,所以仅有两种情况,翻或不翻。

2.翻的顺序对结果没有影响。

3.翻所有的白还是所有的黑?如何翻?——暴搜事实上是不需要考虑这个问题的——翻所有16个格,直到出现全白或者全黑

 

 

二、思路:

生成16全排列,01分别代表翻或不翻该位,如果某种排列方式达到结果状态,则更新最小状态,最后输出结果。

 

中间最关键的一步就是生成子集。

注:事实上,对数组应该是生成子集(翻的位置集合),对位运算是生成全排列(所有位置的状态的全排列)。

生成集合的子集就是生成位运算全排列。

方法一:数组+递归

暴搜复杂度分析:因为只有十六个格子,每个格子有翻和不翻这两种情况,所以你哪怕枚举每一种情况,总的枚举次数也不过是2^16=65536次,所以在时间复杂度上是不会产生问题的。

 

递归模板:

Digui(){

      If (递归到最后一层即递归边界) {判断最终数据是否满足标准状态};

      Else{

            For(循环次数为该层分支数){ 

                  处理该分支;

                  Digui(递归到下一层);

}   ·       

}//else

}//digui

 

  1 /*
  2 POJ 1753 Flip Game (递归枚举)
  3 By Microgoogle
  4 */
  5 #include <stdio.h>
  6 #include <stdlib.h>
  7 
  8 //判断是否达到结果状态:所有都是白的,或者所有都是黑的
  9 int all_white_or_black(int* bits, int len)
 10 {
 11   int i = 0;
 12   for (i = 0; i < len - 1; i++)
 13     if (bits[i] != bits[i + 1])
 14       return 0;
 15   return 1;
 16 }
 17 
 18 //改变一个格子的颜色,并根据其所在位置改变其周围格子的颜色
 19 void change_color(int* arr, int i)
 20 {
 21   arr[i] = !(arr[i]);
 22   int x = i/4;
 23   int y = i%4;
 24   if (y < 3)
 25     arr[i + 1] = !(arr[i + 1]);
 26   if (y > 0)
 27     arr[i - 1] = !(arr[i - 1]);
 28   if (x > 0)
 29     arr[i - 4] = !(arr[i - 4]);
 30   if (x < 3)
 31     arr[i + 4] = !(arr[i + 4]);
 32 }
 33 
 34 //递归生成子集
 35 //这个完全用了前一篇文章的递归方法,只是在else语句中添加了整个图形是否为纯色的判断而已
 36 void combine(int* arr, int len, int* result, int count, const int NUM, int* last)
 37 {
 38   int i;
 39   for (i = len; i >= count; i--)//对子集元素数循环
 40   {
 41     result[count - 1] = i - 1;
 42     if (count > 1)
 43       combine(arr, i - 1, result, count - 1, NUM, last);
 44     else
 45     {
 46       int j = 0;
 47 
 48       //在这里生成arr的副本
 49       int* new_arr = (int*)malloc(sizeof(int)*16);
 50       for (j = 0; j < 16; j++)
 51         new_arr[j] = arr[j];
 52 
 53       for (j = NUM - 1; j >=0; j--){
 54          change_color(new_arr, result[j]);
 55       }//for j
 56       if (all_white_or_black(new_arr, 16)) {
 57          *last = NUM;
 58          free(new_arr);
 59          break;
 60       }//if
 61       free(new_arr);
 62     }//else
 63   }//for i
 64 }//combine
 65 
 66 int main()
 67 {
 68   char str[5];
 69   int bits[16];
 70   int count = 15;
 71   int lines = 4;
 72 
 73 //读入
 74   while (lines--)
 75   {
 76     scanf("%s", str);
 77     int i;
 78     for (i = 0; i < 4; i++)
 79     {
 80       if (str[i] == 'b')
 81         bits[count--] = 1;
 82       else
 83         bits[count--] = 0;
 84     }
 85   }
 86 
 87   if (all_white_or_black(bits, 16))
 88     printf("%d\n", 0);
 89   else
 90   {
 91     //生成bits数组的副本
 92     int* new_bits = (int*)malloc(sizeof(int)*16);
 93     int i;
 94     for (i = 0; i < 16; i++)
 95       new_bits[i] = bits[i];
 96     int j;
 97     //这里last用来接受combine函数里面的NUM,即需要的步数
 98     int last = 0;
 99     for (j = 1; j <= 16; j++){
100       int* result = (int*)malloc(sizeof(int)*j);
101       combine(new_bits, 16, result, j, j, &last);
102       if (last == j){
103         printf("%d\n", last);
104         break;
105       }//if
106       //new_bits已被改变,所以要还原为bits
107       for (i = 0; i < 16; i++)
108         new_bits[i] = bits[i];
109      
110       free(result);
111     }//for j
112     free(new_bits);
113 
114     if (j == 17)  printf("Impossible\n");
115   }// 16
116  
117   return 0;
118 }
119 
120 
121 写的不好,生成子集那步写复杂了。
数组运算

 

 

方法二:位运算+简单循环         //位运算:freedom\个人经验\位运算及用例

位运算:

存储这个字符数组的时候利用一个value值来存储,对value进行位运算操作,枚举每一种翻与不翻的情况,记录每一次使得value == 0||value == 65535的值,得出最后步数最小的结果。

 相比数组的方法,位运算最大的优势并不在于空间复杂度上,而是简单操作速度快并避免递归生成全排列。

 

 1 //1753
 2 #include <iostream>
 3 #define MAX 999999
 4 using namespace std;
 5 char s[4][4];
 6 int cs[16] = {0x13,0x27,78,140,305,626,1252,2248,4880,8992,20032,35968,12544,29184,58368,51200};
 7 int po[16] = {1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};
 8 int main()
 9 {
10     int i,j,value = 0;
11     int cmin = MAX;
12     char c;
13     for(i = 0;i < 16;i++)
14     {
15         cin >> c;
16         if(c == 'b')
17             value += (int)po[i];
18         else    continue;
19     }
20 
21     for(i = 0;i < 65536;i++)
22     {
23         int cou = 0;
24         int cvalue = value;
25         for(j = 0;j < 16;j++)
26             if(i & (int)po[j])
27             {
28                 cou++;
29                 cvalue ^= cs[j];//太经典了!对每一种翻法计算对应十进制加的数!然后做异或!异或就是部分取反!A与B异或就是A中B是1的位取反!!!
30             }
31         if(cvalue == 0 || cvalue == 65535)
32             if(cou < cmin)  cmin = cou;
33     }
34     if(cmin == MAX) cout << "Impossible";
35     else cout << cmin << endl;
36    return 0;
37 }
位运算

 

posted @ 2015-09-16 00:17  Travelller  阅读(118)  评论(0编辑  收藏  举报