POJ3279Fliptile
题意&思路:
有一个m*n的01矩阵,每次选一个格子会翻转这个格子和他的上下左右。问是否能将所有的格子变成0,能就输出翻转次数最小的方案(有多个即输出字典序最小的),0表示不反转,1表示翻转。如果没有输出“IMPOSSIBLE"。
这是kuangbin的搜索题单里的一道题,之前一直没有思路。看了其他人的题解,其实就是枚举状态,然后可以发现如果(i-1,j)点的值是1,就通过翻转(i,j)来改变。就这样通过一层层的枚举来保证上一行全是0,最后判断最后一行是不是都为0就可以了。
代码:
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
#include<ctype.h>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#define pii pair<int,int>
#define ll long long
#define cl(x,y) memset(x,y,sizeof(x))
#define ct cerr<<"Time elapsed:"<<1.0*clock()/CLOCKS_PER_SEC<<"s.\n";
const int N=1e6+10;
const int mod=1e7+9;
const int maxn=0x3f3f3f3f;
const int minn=0xc0c0c0c0;
const int inf=99999999;
using namespace std;
int maze[20][20],pre[20][20],ans[20][20],temp[20][20],dis[5][2]={{0,0},{1,0},{-1,0},{0,1},{0,-1}};
int n,m;
int step,res=maxn;
void change(int x,int y)
{
int i;
for(i=0;i<5;i++)
temp[x+dis[i][0]][y+dis[i][1]]^=1;
return;
}
int solve()
{
int i,j;
memcpy(temp,maze,sizeof(maze));
for(i=1;i<=m;i++)
if(pre[1][i])
{
change(1,i);
step++;
}
for(i=2;i<=n;i++)
for(j=1;j<=m;j++)
if(temp[i-1][j])
{
pre[i][j]=1;
change(i,j);
step++;
}
for(i=1;i<=m;i++)
if(temp[n][i])
return 0;
return 1;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int i,j;
cin>>n>>m;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
cin>>maze[i][j];
for(i=0;i<(1<<m);i++)
{
cl(pre,0);
step=0;
for(j=0;j<m;j++)
pre[1][m-j]=(i>>j)&1;
if(solve() && step>0 && res>step)
{
res=step;
memcpy(ans,pre,sizeof(pre));
}
}
if(res==maxn)
cout<<"IMPOSSIBLE"<<endl;
else
{
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
cout<<ans[i][j]<<" ";
cout<<endl;
}
}
return 0;
}