[Usaco2007 Open]Fliptile 翻格子游戏 状态压缩

来源:互联网 发布:淘宝上兼职是真的吗 编辑:程序博客网 时间:2024/05/18 01:55

考试想到了状压,苦于T1废掉太长时间,于是默默输出impossible。。
我们知道,一个格子的翻转受其翻转次数和它相邻翻转次数的影响。
由每一个位置操作两次相当于把它翻过来又翻回去,所以答案中每一个点操作次数为0或1。
然后我们枚举第一行的状态,1代表翻转,0代表不翻转。
如果与它相连的点的操作次数和它本身状态之和为偶数,它就会被翻成白色。
由于我们从上向下推,所以对于上一行的点来说,只有它下面那一个点不确定,我们就让下面这一个点进行能够让上一行点满足全为白色的操作。
这样推到最后一行,前面m-1行都满足,我们只需要看最后一行是否满足就可以了。
最后一行满足,就说明这种方案合法,再去更新之前存下的合法方案就可以了。

例如此位置本身与上左右状态之和为偶数,而此位置在初始时是1,所以我们需要把它翻成0,需要奇数个操作,所以我们把它下面的状态设置为1即可。

#include<iostream>#include<cstdio>#include<cstring>using namespace std;#define pos(i,a,b) for(int i=(a);i<=(b);i++)#define N 20int n,m;int a[N][N],temp[N][N],ans[N][N];int work(int i){pos(j,1,n){if(j==1){pos(k,1,m)if((1<<(m-k))&i){temp[j][k]=1;}}pos(k,1,m){if(j!=n){if((temp[j][k-1]+temp[j][k+1]+temp[j-1][k]+temp[j][k])%2==0){if(a[j][k]){temp[j+1][k]=1;}else{temp[j+1][k]=0;}}else{if(a[j][k]==0){temp[j+1][k]=1;}else{temp[j+1][k]=0;}}}if(j==n){if((temp[j][k-1]+temp[j][k+1]+temp[j-1][k]+temp[j][k])%2==0){if(a[j][k]){return 0;}}else{if(!a[j][k])  return 0;}}}}return 1;}int flag;int count(){int sum1=0,sum2=0;;pos(i,1,n){pos(j,1,m){if(temp[i][j])  sum1++;if(ans[i][j])sum2++;}}if(sum1<sum2)  return 1;return 0;}void update(){if(flag){if(count()){pos(i,1,n)pos(j,1,m)ans[i][j]=temp[i][j];}else{pos(i,1,n){pos(j,1,m){if(temp[i][j]<ans[i][j]){pos(k,1,n){pos(l,1,m){ans[k][l]=temp[k][l];}}}else{return;}}}}}else{pos(i,1,n)pos(j,1,m)ans[i][j]=temp[i][j];flag=1;}}int main(){//freopen("fliptile.in","r",stdin);    //freopen("fliptile.out","w",stdout);scanf("%d%d",&n,&m);pos(i,1,n){pos(j,1,m){scanf("%d",&a[i][j]);}}pos(i,0,(1<<m)-1){memset(temp,0,sizeof(temp));if(work(i)){update();}else{continue;}}if(flag){pos(i,1,n){pos(j,1,m){cout<<ans[i][j]<<" ";}cout<<endl;}}else{cout<<"IMPOSSIBLE";}return 0;}


原创粉丝点击