zoukankan      html  css  js  c++  java
  • poj1753 Flip Game —— 二进制压缩 + dfs / bfs or 递推

    题目链接:http://poj.org/problem?id=1753



     

    Flip Game
    Time Limit: 1000MS   Memory Limit: 65536K
    Total Submissions: 46724   Accepted: 20002

    Description

    Flip game is played on a rectangular 4x4 field with two-sided pieces placed on each of its 16 squares. One side of each piece is white and the other one is black and each piece is lying either it's black or white side up. Each round you flip 3 to 5 pieces, thus changing the color of their upper side from black to white and vice versa. The pieces to be flipped are chosen every round according to the following rules: 
    1. Choose any one of the 16 pieces. 
    2. Flip the chosen piece and also all adjacent pieces to the left, to the right, to the top, and to the bottom of the chosen piece (if there are any).

    Consider the following position as an example: 

    bwbw 
    wwww 
    bbwb 
    bwwb 
    Here "b" denotes pieces lying their black side up and "w" denotes pieces lying their white side up. If we choose to flip the 1st piece from the 3rd row (this choice is shown at the picture), then the field will become: 

    bwbw 
    bwww 
    wwwb 
    wwwb 
    The goal of the game is to flip either all pieces white side up or all pieces black side up. You are to write a program that will search for the minimum number of rounds needed to achieve this goal. 

    Input

    The input consists of 4 lines with 4 characters "w" or "b" each that denote game field position.

    Output

    Write to the output file a single integer number - the minimum number of rounds needed to achieve the goal of the game from the given position. If the goal is initially achieved, then write 0. If it's impossible to achieve the goal, then write the word "Impossible" (without quotes).

    Sample Input

    bwwb
    bbwb
    bwwb
    bwww

    Sample Output

    4
    

    Source

     


    方法一:

    1.对于每一个格子,它都有翻或不翻两种选择,所以枚举从第一个格子开始一直到最后一格,所以总共有2^16种情况(像二叉树)。然而这种方式必须要搜索到最后一个才能作出判断。

    2.对于整盘棋,.每次枚举翻一个,第一次有16种选择,第二次有15种选择,所以:16*15*14……。当搜索到符合全白或全黑则直接返回,这种方法较快。

    技巧:由于每个棋只有两种情况,则可以讲棋盘压缩成二进制以记录状态。(推导:当单个的情况数为n种时,可以将其状态用n进制记录)。


    方法二:

    1.枚举第一行的所有状态。

    2.如果把所有棋翻为黑色:从第二行开始,如果当前格子的头上格子为白色,那么需要翻转当前格子。因为在这一行之内,只有当前格子能够使头上的格子变为黑色,然后一直递推到最后一行。如果把所有棋翻为白色,做法亦如此。

    3.分析:方法二相对于方法一是有明显的优势的:方法二从第二行开始,每翻转一个棋都是有针对性的、有目的性的;而方法一则是枚举所有情况,盲目地翻转,如:假如要把所有棋翻为黑色,而当前格子为黑色,却还是硬要翻转该格子,那显然是行不通的。所以方法二相对来说更“智能”。

    4.注意:s&(1<<i)的值要么等于0,要么等于(1<<i);而不是0或1。应注意!!

    5.此题的加强版,必须使用方法二:POJ3279:http://www.cnblogs.com/DOLFAMINGO/p/7538582.html




    DFS(方法一):

     1 #include<cstdio>//dfs直接对棋盘进行操作
     2 #include<cstring>
     3 #define MIN(a,b) (a<b?a:b)
     4 #define init_min 2000000000
     5 int a[20],min;
     6 
     7 int get()
     8 {   //获取状态的二进制数表达
     9     int sum = 0;
    10     for(int i = 0; i<16; i++)
    11         sum += a[i]<<i;
    12     return sum;
    13 }
    14 
    15 void flip(int loc)
    16 {
    17     a[loc] = !a[loc];
    18     if(loc-4>=0) a[loc-4] = !a[loc-4];
    19     if(loc+4<16) a[loc+4] = !a[loc+4];
    20     if(loc%4!=0) a[loc-1] = !a[loc-1];
    21     if(loc%4!=3) a[loc+1] = !a[loc+1];
    22 }
    23 
    24 void dfs(int loc,int step)
    25 {
    26     if(loc==16)
    27     {   //当搜索到最后一个格子时,开始判断
    28         int status = get();
    29         if(status==0 || status==(1<<16)-1)
    30             min = MIN(min,step);
    31     }
    32 
    33     else
    34     {
    35         dfs(loc+1,step);
    36         flip(loc);//翻转
    37         dfs(loc+1,step+1);
    38         flip(loc);//再翻转,使其状态复原
    39     }
    40 }
    41 
    42 int main()
    43 {
    44     char s[10];
    45     for(int i = 0; i<4; i++)
    46     {
    47         scanf("%s",s);
    48         for(int j = 0; j<4; j++)
    49         {
    50             if(s[j]=='b') a[i*4+j] = 1;
    51             else a[i*4+j] = 0;
    52         }
    53     }
    54     min = init_min;
    55     dfs(0,0);
    56     if(min!=init_min)
    57         printf("%d
    ",min);
    58     else
    59         puts("Impossible");
    60     return 0;
    61 }
    View Code


    BFS(方法一):

     1 #include<cstdio>//由于bfs需要记录状态,而直接用数组入队列会增加时间(stl的原因),所以用二进制数记录,并对其操作  
     2 #include<cstring>  
     3 #include<queue>  
     4   
     5 using namespace std;  
     6   
     7 int a[20],ss,vis[66000];//ss为初始状态,vis用来标记访问过的状态  
     8 struct node  
     9 {   //status为棋牌状态,step为步数  
    10     int status, step;  
    11 };  
    12   
    13 void calcul(node *now, node *next,int i)  
    14 {  
    15     if(now->status&(1<<i)) next->status -= (1<<i);  
    16     else  next->status += (1<<i);  
    17 }  
    18   
    19 void turn (node *now, node *next,int i)  
    20 {   //翻转时直接对二进制数进行操作,即01  
    21     calcul(now, next,i);  
    22     if(i-4>0) calcul(now, next,i-4);  
    23     if(i+4<16) calcul(now, next,i+4);  
    24     if(i%4!=0) calcul(now, next,i-1);  
    25     if(i%4!=3) calcul(now, next,i+1);  
    26 }  
    27   
    28 int bfs()  
    29 {  
    30     node now, next;  
    31     queue<node>q;  
    32     now.status = ss, now.step = 0;  
    33   
    34     q.push(now);  
    35     vis[now.status] = 1;  
    36     memset(vis,0,sizeof(vis));  
    37     while(!q.empty())  
    38     {  
    39         now = q.front(); q.pop();  
    40         if(now.status==0 || now.status==0xffff) return now.step;  
    41         for(int i = 0; i<16; i++)  
    42         {   /*之前在这里加了个是否重复翻转的判断,但时间增加了一倍 
    43             细想下面的判重已经包含了这个功能*/  
    44             next.status = now.status;  
    45             turn(&now,&next,i);//翻转  
    46             if(vis[next.status]) continue;//判重  
    47             next.step = now.step + 1;  
    48             vis[next.status] = 1;  
    49             if(next.status==0 || next.status==0xffff) return next.step;  
    50             q.push(next);  
    51         }  
    52     }  
    53     return -1;  
    54 }  
    55   
    56 int main()  
    57 {  
    58     char s[10];  
    59     ss = 0;  
    60     for(int i = 0; i<4; i++)  
    61     {  
    62         scanf("%s",s);  
    63         for(int j = 0; j<4; j++)  
    64         {  
    65             if(s[j]=='b') { a[i*4+j] = 1; ss += 1<<(i*4+j);}  
    66             else a[i*4+j] = 0;  
    67         }  
    68     }  
    69     int ans = bfs();  
    70     if(ans!=-1)  
    71         printf("%d
    ",ans);  
    72     else  
    73         puts("Impossible");  
    74     return 0;  
    75 }  
    View Code

    方法二:

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <cmath>
     5 #include <algorithm>
     6 #include <vector>
     7 #include <queue>
     8 #include <stack>
     9 #include <map>
    10 #include <string>
    11 #include <set>
    12 #define ms(a,b) memset((a),(b),sizeof((a)))
    13 using namespace std;
    14 typedef long long LL;
    15 const int INF = 2e9;
    16 const LL LNF = 9e18;
    17 const int MOD = 1e9+7;
    18 const int MAXN = 15+5;
    19 
    20 int M[MAXN][MAXN], subM[MAXN][MAXN];
    21 int op[MAXN][MAXN];
    22 int n, m;
    23 
    24 void press(int x, int y)
    25 {
    26     op[x][y] = 1;
    27     subM[x][y] = !subM[x][y];
    28     if(x-1>=0) subM[x-1][y] = !subM[x-1][y];
    29     if(x+1<4)  subM[x+1][y] = !subM[x+1][y];
    30     if(y-1>=0) subM[x][y-1] = !subM[x][y-1];
    31     if(y+1<4)  subM[x][y+1] = !subM[x][y+1];
    32 }
    33 
    34 bool all()
    35 {
    36     int b1 = 1, b2 = 1;
    37     for(int i = 0; i<4; i++)
    38     for(int j = 0; j<4; j++)
    39     {
    40         if(subM[i][j]==0)  b1 = 0;
    41         if(subM[i][j]==1)  b2 = 0;
    42     }
    43     return b1||b2;
    44 }
    45 
    46 void solve(int s, int type, int &ans)
    47 {
    48     ms(op, 0);
    49     memcpy(subM, M, sizeof(subM));
    50 
    51     int cnt = 0;
    52     for(int i = 0; i<4; i++)
    53         if( (s&(1<<i))==(type<<i) ) press(0, i), cnt++;
    54         //注意:s&(1<<i)的值要么等于0,要么等于(1<<i);而不是0或1。应注意!!
    55 
    56     for(int i = 1; i<4; i++)
    57     for(int j = 0; j<4; j++)
    58         if(subM[i-1][j]==type)  press(i, j), cnt++;
    59 
    60     if(all())
    61         ans = min(ans, cnt);
    62 }
    63 
    64 int main()
    65 {
    66     char s[10];
    67     for(int i = 0; i<4; i++)
    68     {
    69         scanf("%s",s);
    70         for(int j = 0; j<4; j++)
    71         {
    72             if(s[j]=='b') M[i][j] = 1;
    73             else M[i][j] = 0;
    74         }
    75     }
    76 
    77     int ans = INF;
    78     for(int s = 0; s<(1<<4); s++)
    79     {
    80         solve(s, 0, ans);    //尝试把棋全部翻成0
    81         solve(s, 1, ans);    //尝试把棋全部翻成1
    82     }
    83     if(ans!=INF)
    84         printf("%d
    ", ans);
    85     else
    86         puts("Impossible");
    87 }
    View Code




  • 相关阅读:
    序列化
    执行mysql脚本
    MinGW-notepad++开发c/c++程序
    MySql免安装版配置方法
    Wamp 简单使用方法
    [锋利JQ]-图片提示效果
    [锋利的JQ]-超链接提示效果
    PHPcms 系统简单使用
    NC帮助文档网址
    NC的开发模型
  • 原文地址:https://www.cnblogs.com/DOLFAMINGO/p/7538788.html
Copyright © 2011-2022 走看看