zoukankan      html  css  js  c++  java
  • 【模拟+递归+位运算】POJ1753-Flip Game

    由于数据规模不大,利用爆搜即可。第一次用位运算写的,但是转念一想应该用递归更加快,因为位运算没有剪枝啊(qДq )

    【思路】

    位运算:时间效率较低(172MS),有些辜负了位运算的初衷。首先将二维数组倒序看作一个二进制数num。我们假设1代表翻转,0代表不翻转,可以发现以下规律:0 xor 1=1,1 xor 1=0;0 xor 0=0,1 xor 0=1,恰巧满足异或运算。我们假设另一个二进制数i∈[0,2^16),通过异或运算就可以模拟出所有清形。

    用check和i进行&操作可以求出以哪些位置为中心进行翻转。假设当前要翻转的方格在二进制数num中所在位数为k,则翻转它时同时翻转的四个方格(假设存在的话)分别为(k-1),(k+1),(k-4),(k+4),则turn[k]=在这几位为1,其余均为0的二进制数,我在草稿纸上手工计算之后直接设在数组中。这样的好处在于,用turn与now直接进行异或操作,即可求出翻转后的情形。

    之后通过求i的二进制中1的个数即可。这步操作有一个较为简便的方法,通过草稿纸上模拟就可以领悟。

     1 {
     2     int count = 0;
     3 
     4     while(x)
     5     {
     6         x = x & ( x - 1 );
     7         count++; 
     8     }
     9     printf("count = %d/n", count);
    10 }

    上述方法非常实用,要牢记。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 const int INF=100000;
     6 int num,min;
     7 int check[16]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};
     8 int turn[16]={18,37,74,132,289,594,1188,2120,4624,9504,19008,33920,8448,20992,41984,18432};
     9 
    10 void init()
    11 {
    12     int k=1;
    13     for (int i=0;i<4;i++)
    14     {
    15         for (int j=0;j<4;j++)
    16         {
    17             char c;
    18             scanf("%c",&c);
    19             if (c=='w') num+=k;
    20             k*=2;
    21         }
    22         getchar();
    23     }
    24 }
    25 
    26 int mainprocess()
    27 {
    28     int min=INF;
    29     for (int i=0;i<65535;i++)
    30     {
    31         int now=num^i;
    32         for (int j=0;j<16;j++)
    33             if (check[j]&i) 
    34             {
    35                 now=now^turn[j];
    36             }
    37         if (now==0 || now==65535)
    38         {
    39             int ans=0,x=i;
    40             while (x)
    41             {
    42                 x=x & (x-1);
    43                 ans++;
    44             }
    45             if (ans<min) min=ans;
    46         }
    47     }
    48     if (min==INF) return -1;
    49         else return min;
    50 }
    51 
    52 int main()
    53 {
    54    init();
    55    int output=mainprocess();
    56    if (output==-1) cout<<"Impossible"; else cout<<output;
    57    cout<<endl;
    58    return 0;
    59 }

    此外还可以通过递归+剪枝来完成,效率较高(47ms),计算量较小。值得注意的是c++中如果将数组传入子程序,传入的其实是地址。所以必须在子程序中设置临时数组来保存当前状态,回溯时再还给原来的数组。注意,这个临时数组必须在子程序中,我第一次写的时候误将它写在了主程序中,那么临时数组就完全失去了它的意义。

    剪枝:如果当前情况下需要翻转的次数已经小于最小值,则递归不需要再继续。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cmath>
     4 using namespace std;
     5 const int INF=100000;
     6 int map[4][4];
     7 int dx[4]={0,0,1,-1};
     8 int dy[4]={1,-1,0,0};
     9 int ans;
    10 
    11 void recurrence(int step,int turn)
    12 {
    13     int tempmap[4][4];
    14     if (turn>=ans) return;
    15     if (step==16)
    16     {
    17         int sum=0;
    18         for (int i=0;i<4;i++)
    19             for (int j=0;j<4;j++) sum+=map[i][j];
    20         if (sum==0 || sum==16) ans=turn;
    21     }
    22     else
    23     {
    24         for (int k1=0;k1<4;k1++) 
    25             for (int k2=0;k2<4;k2++) tempmap[k1][k2]=map[k1][k2];
    26             
    27         for (int i=0;i<2;i++)
    28         {
    29             if (i==1)
    30             {
    31                 int x=step/4,y=step%4;
    32                 map[x][y]=1-map[x][y];
    33                 for (int d=0;d<4;d++)
    34                     if (x+dx[d]>=0 && x+dx[d]<4 && y+dy[d]>=0 && y+dy[d]<4) map[x+dx[d]][y+dy[d]]=1-map[x+dx[d]][y+dy[d]];
    35             }
    36             recurrence(step+1,turn+i);
    37             for (int k1=0;k1<4;k1++) 
    38                 for (int k2=0;k2<4;k2++) map[k1][k2]=tempmap[k1][k2];
    39         }
    40     }
    41 }
    42 
    43 int main()
    44 {
    45     ans=INF;
    46     for (int i=0;i<4;i++)
    47     {
    48         char c;
    49         for (int j=0;j<4;j++)
    50         {
    51             scanf("%c",&c);
    52             if (c=='w') map[i][j]=1; else map[i][j]=0;
    53         }
    54         getchar();
    55     }
    56     recurrence(0,0);
    57     if (ans!=INF) cout<<ans<<endl;else cout<<"Impossible"<<endl;
    58 }
  • 相关阅读:
    Pycharm2017应用小技巧
    浅谈哈希表
    攻克网页文字不可复制的难题
    Java中List的相关知识
    电脑实用小技巧
    Jme3涉及的eclipse知识
    Word2010撤销按钮失效,Ctrl+Z失效解决办法
    Word文档中怎么删除空白页?删除空白页的六种方法
    word中分栏后文字均匀的分布在了左右两栏,而不是填满左栏再填右栏,怎么办?
    visdom服务启动时提示Downloading scripts, this may take a little while解决办法
  • 原文地址:https://www.cnblogs.com/iiyiyi/p/4687818.html
Copyright © 2011-2022 走看看