1. 程式人生 > >BZOJ 1647 [Usaco2007 Open]Fliptile 翻格子遊戲:部分枚舉 位運算

BZOJ 1647 [Usaco2007 Open]Fliptile 翻格子遊戲:部分枚舉 位運算

define col turn com possible 最小 多少 格子 emc

題目鏈接: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 翻格子遊戲:部分枚舉 位運算