BZOJ 1647 [Usaco2007 Open]Fliptile 翻格子游戏:部分枚举 位运算
Posted Leohh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 1647 [Usaco2007 Open]Fliptile 翻格子游戏:部分枚举 位运算相关的知识,希望对你有一定的参考价值。
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1647
题意:
在一个n*m(1 <= n,m <= 15)的棋盘上,每一个格子里都有一个可以翻转的棋子。
棋子的一面是黑色,一面是白色。
若翻转一个棋子,则它周围的四个棋子也会被翻转。
问你最少需要多少次翻转,使所有的棋子都变成白面向上。
如果可以做到,输出字典序最小的结果(将结果当成字符串处理)。如果不能做到,输出“IMPOSSIBLE”。
题解:
首先有一个结论:
如果第i-1行第j列的棋子为黑,那么第i行j列的棋子一定会被翻转,因为只有这样上一行的黑棋子才能变成白棋子。
所以,如果上一行的棋子状态已经确定,那么当前行的翻转方案是唯一确定的。
因此,如果第1行的棋子状态确定,接下来2到n行的方案也都唯一确定了。
所以只用枚举第1行的棋子状态,复杂度O(2^15)。
注:如果用状态压缩state枚举表示第一行的状态的话,state的第0位代表棋盘的第m-1列。
因为要按字典序从小到大枚举。
AC Code:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #define MAX_N 20 5 #define INF 10000000 6 7 using namespace std; 8 9 const int dx[]={-1,1,0,0}; 10 const int dy[]={0,0,-1,1}; 11 12 int n,m; 13 int a[MAX_N][MAX_N]; 14 int t[MAX_N][MAX_N]; 15 int cnt[MAX_N][MAX_N]; 16 int ans[MAX_N][MAX_N]; 17 bool failed=false; 18 19 void read() 20 { 21 cin>>n>>m; 22 for(int i=0;i<n;i++) 23 { 24 for(int j=0;j<m;j++) 25 { 26 cin>>a[i][j]; 27 } 28 } 29 } 30 31 inline bool is_legal(int x,int y) 32 { 33 return x>=0 && x<n && y>=0 && y<m; 34 } 35 36 void solve() 37 { 38 int minn=INF; 39 for(int state=0;state<(1<<m);state++) 40 { 41 memset(cnt,0,sizeof(cnt)); 42 memcpy(t,a,sizeof(int)*MAX_N*MAX_N); 43 int tot=0; 44 for(int j=0;j<m;j++) 45 { 46 int pos=m-j-1; 47 if((state>>pos)&1) 48 { 49 t[0][j]^=1; 50 cnt[0][j]=1; 51 tot++; 52 for(int k=0;k<4;k++) 53 { 54 int x=dx[k]; 55 int y=j+dy[k]; 56 if(is_legal(x,y)) t[x][y]^=1; 57 } 58 } 59 } 60 for(int i=1;i<n;i++) 61 { 62 for(int j=0;j<m;j++) 63 { 64 if(t[i-1][j]) 65 { 66 t[i][j]^=1; 67 cnt[i][j]=1; 68 tot++; 69 for(int k=0;k<4;k++) 70 { 71 int x=i+dx[k]; 72 int y=j+dy[k]; 73 if(is_legal(x,y)) t[x][y]^=1; 74 } 75 } 76 } 77 } 78 bool is_white=true; 79 for(int j=0;j<m;j++) 80 { 81 if(t[n-1][j]) 82 { 83 is_white=false; 84 break; 85 } 86 } 87 if(is_white && tot<minn) 88 { 89 memcpy(ans,cnt,sizeof(int)*MAX_N*MAX_N); 90 minn=tot; 91 } 92 } 93 if(minn==INF) failed=true; 94 } 95 96 void print() 97 { 98 if(failed) 99 { 100 cout<<"IMPOSSIBLE"<<endl; 101 return; 102 } 103 for(int i=0;i<n;i++) 104 { 105 for(int j=0;j<m;j++) 106 { 107 cout<<ans[i][j]; 108 if(j!=m-1) cout<<" "; 109 } 110 cout<<endl; 111 } 112 } 113 114 int main() 115 { 116 read(); 117 solve(); 118 print(); 119 }
以上是关于BZOJ 1647 [Usaco2007 Open]Fliptile 翻格子游戏:部分枚举 位运算的主要内容,如果未能解决你的问题,请参考以下文章
bzoj 1647: [Usaco2007 Open]Fliptile 翻格子游戏dfs
[BZOJ1711][Usaco2007 Open]Dining吃饭
BZOJ 1711: [Usaco2007 Open]Dining吃饭
[BZOJ1710][Usaco2007 Open]Cheappal 廉价回文