Fliptil(fliptile.pas/c/cpp)
【问题描述】
约翰知道,那些高智力又快乐的奶牛产奶量特别高。所以他做了一个翻瓦片的益智游戏来娱乐奶牛。
在一个M×N的骨架上,每一个格子里都有一个可以翻转的瓦片。瓦片的一面是黑色的,而另一面是白色的。对一个瓦片进行翻转,可以使黑变白,也可以使白变黑。然而,奶牛们的蹄子是如此的巨大而且笨拙,所以她们翻转一个瓦片的时候,与之有公共边的相邻瓦片也都被翻转了。
那么,这些奶牛们最少需要多少次翻转,使所有的瓦片都变成白面向上呢?如果可以做到,输出字典序最小的结果(将结果当成字符串处理),如果不能做到输出"IMPOSSIBLE".
【输入格式】
第1行输入M和N;之后M行N列,输入游戏开始时的瓦片状态,0表示白面向上,1表示黑面向上。
【输出格式】
输出M行,每行N个用空格隔开的整数,表示对应的格子是否进行了翻动,0表示不翻动,1表示翻动。
【输入样例】
4 4
1 0 0 1
0 1 1 0
0 1 1 0
1 0 0 1
【输出样例】
0 0 0 0
1 0 0 1
1 0 0 1
0 0 0 0
【数据规模】
对于50%的数据:1≤M,N≤5;
对于70%的数据:1≤M,N≤10;
对于100%的数据:1≤M,N≤15;
看数据范围应该也猜得到是状态压缩+DFS。
一开始直接枚举第一行的状态,即翻或不翻的状态(状压)。
因为如果上一行是1,那么这一格一定要翻。
N^2做一遍即可。
理论复杂度O((2^M)*(N^2))
code:
#include <cstdio> using namespace std; char tc(){ static char fl[1000000],*A=fl,*B=fl; return A==B&&(B=(A=fl)+fread(fl,1,1000000,stdin),A==B)?EOF:*A++; } int read(){ char c;while(c=tc(),c<'0'||c>'9');int x=c-'0'; while(c=tc(),c>='0'&&c<='9')x=(x<<1)+(x<<3)+c-'0'; return x; } int N,M,a[16][16],t[16][16],ans[16][16],w[16][16],res=2e9; void rotate(int x,int y){ t[x][y]^=1;w[x][y]=1; if(x-1>=1)t[x-1][y]^=1;if(y-1>=1)t[x][y-1]^=1; if(x+1<=N)t[x+1][y]^=1;if(y+1<=M)t[x][y+1]^=1; } void check(int wks){ int tk=0; for(int i=1;i<=N;i++)for(int j=1;j<=M;j++)w[i][j]=0,t[i][j]=a[i][j]; for(int i=1;i<=M;i++) if(1<<i-1&wks){ tk++;if(tk>=res)return ;//如果大于最优解直接return ; rotate(1,M-i+1); }//旋转第一行的(按wks状态旋转) for(int i=2;i<=N;i++) for(int j=1;j<=M;j++) if(t[i-1][j]){ tk++;if(tk>=res)return ; rotate(i,j); } for(int j=1;j<=M;j++)if(t[N][j])return ; res=tk; for(int i=1;i<=N;i++) for(int j=1;j<=M;j++)ans[i][j]=w[i][j]; } void puts(){ for(int i=1;i<=N;i++){ for(int j=1;j<=M;j++) printf("%d ",ans[i][j]); puts(""); } } int main(){ freopen("fliptile.in","r",stdin); freopen("fliptile.out","w",stdout); N=read(),M=read(); for(int i=1;i<=N;i++) for(int j=1;j<=M;j++)a[i][j]=read(); for(int i=0;i<(1<<M);i++) check(i); if(res==2e9)puts("IMPOSSIBLE"); else puts(); }