poj 3279 fliptile (翻轉棋盤,列舉方法)
阿新 • • 發佈:2018-12-11
思路:
翻轉偶數次跟翻轉0次是一樣的,奇數次是跟1次是一樣的,所以最後的結果中只可能有0,1;
下一行的狀態可以通過前二行的的狀態得到,因為在求(i,j)是否要翻轉的時候,(i-1,j)(i-1,j-1)(i-1,j+1)(i-2)(j)是否翻轉都已經知道了,並且要(i-1,j)這個位置是0,那麼就可以推出(i,j)這個位置是否要翻轉,所以其實問題中只要列舉第一行的所有情況,就能推出所有的情況,並且一共最多隻有2^15種;
列舉方法要保證最後的答案是字典序最小的,那麼可以用二進位制來表示每一位的狀態(0----2^n),第i種情況下,第j個位置的值應該是i>>j&1(不明白的可以自己推導一下),如果答案是1那麼就說明要進行翻轉,如果是0,就不用;
程式碼如下:
#include <iostream> #include <string> #include <vector> #include <set> #include <cstring> #include <climits> #include <algorithm> #include <cmath> #include <queue> #include <stdio.h> #define esp 1e-4 using namespace std; int ini[20][20]; int flip[20][20]; int final_ans[20][20]; int m,n; int main() { while(cin>>m>>n) { int ans=1e8; int time=0;//要翻轉的次數 for(int i=1;i<=m;i++) { for(int j=1;j<=n;j++) { cin>>ini[i][j]; } } int ok=0; for(int i=0;i<(1<<n);i++) { memset(flip,0,sizeof(flip)); for(int j=1;j<=n;j++) { flip[1][j]=i>>(j-1) &1; if(flip[1][j]) time++; } for(int r=2;r<=m;r++) { for(int c=1;c<=n;c++) { if((ini[r-1][c]+flip[r-1][c]+flip[r-1][c-1]+flip[r-1][c+1]+flip[r-2][c])&1) { flip[r][c]=1; time++; } } } int flag=1; for(int j=1;j<=n;j++) { if((ini[m][j]+flip[m][j]+flip[m-1][j]+flip[m][j-1]+flip[m][j+1])&1) { flag=0; break; } } if(flag && time<ans) { ok=1; ans=time; for(int i=1;i<=m;i++) { for(int j=1;j<=n;j++) { final_ans[i][j]=flip[i][j]; } } } } if(ok) { for(int i=1;i<=m;i++) { cout<<final_ans[i][1]; for(int j=2;j<=n;j++) { cout<<" "<<final_ans[i][j]; } cout<<endl; } } else cout<<"IMPOSSIBLE\n"; } return 0; }