【BZOJ 1647】[Usaco2007 Open]Fliptile 翻格子游戏 模拟、搜索
第一步我们发现对于每一个格子,我们只有翻和不翻两种状态,我们发现一旦确定了第一行操作,那么第二行的操作也就随之确定了,因为第一行操作之后我们要想得到答案就得把第一行全部为0,那么第二行的每一个格子的操作都会对应于其上的格子的改变,而且也只有第二行能救他了。因此我们只要知道第一行的操作其他的就全都可以推知了,于是我们就用状压枚举第一行的操作,因为这是天然字典序。
#include <cstdio> #include <iostream> using namespace std; int A[20],n,m,a[20],full,b[20],ANS[20],god,Ans=0x7fffffff; int main(){ scanf("%d%d",&m,&n); full=(1<<n)-1; for(register int i=1,x;i<=m;i++){ for(register int j=1;j<=n;j++) scanf("%d",&x),a[i]|=(x<<(n-j)); } if(n==1) { b[1]=1; } else { b[1]=3; for(register int i=2;i<n;i++) b[i]=(1<<i)|(1<<(i-1))|(1<<(i-2)); b[n]=(1<<(n-1))|(1<<(n-2)); } for(register int k=0;k<=full;k++) { register int now=a[1],p=k,sum=0; A[1]=k; for(register int i=1;i<=n;i++) if(k&(1<<(i-1)))now^=b[i],sum++; p=now; for(register int i=2;i<=m;i++) { now=a[i]; A[i]=p; for(register int j=1;j<=n;j++) if(A[i-1]&(1<<(j-1)))now^=(1<<(j-1)); for(register int j=1;j<=n;j++) if(p&(1<<(j-1)))now^=b[j],sum++; p=now; } if(!p) { god=1; if(sum<Ans) { for(register int i=1;i<=m;i++)ANS[i]=A[i]; Ans=sum; } } } if(god) { for(int i=1;i<=m;i++) { for(int j=n;j>0;j--) { if(ANS[i]&(1<<(j-1))) printf("1 "); else printf("0 "); } puts(""); } return 0; } printf("IMPOSSIBLE"); }
苟利国家生死以, 岂因祸福避趋之。