POJ 3279 Fliptile
题意:
给定一m*n的01矩阵,每翻动一个格子,它上下左右四个格子也会翻面(0变1,1变0),问最少翻动几次,得全0矩阵。如果最小操作数对应多种操作方案 ,输出字典序最小的方案。如果不能得全0矩阵,输出“IMPOSSIBLE”。
思路:
枚举第一行的操作,然后根据每种操做后的第一行的状态,往下递推后面行的操作(每行的操作由前一行的状态决定),最后判断最后一行是不是全0。
代码:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=20;
int n,m;
bool a[N][N],b[N][N],vis[N][N],ans[N][N];
void flip(int x,int y){
a[x][y]^=1,a[x-1][y]^=1,a[x+1][y]^=1,a[x][y-1]^=1,a[x][y+1]^=1;
}
bool check(){
for(int j=1;j<=n;++j){
if(a[m][j]) return false;
}
return true;
}
int main(){
cin>>m>>n;//m行n列
for(int i=1;i<=m;++i){
for(int j=1;j<=n;++j){
cin>>a[i][j];
}
}
int op=1<<n;//枚举第一行的操作(后续操作就已被递推决定)
int minn=n*m+1;
bool flag=0;
for(int i=0;i<op;++i){
int cnt=0;
memcpy(b,a,sizeof a);
for(int j=0;j<n;++j){ //操作第一行
if(i>>j&1){
flip(1,j+1);//啊我傻缺了!做题专心点好吗。。。= =
vis[1][j+1]=1;
++cnt;
}
}
//由第一行状态,递推完成剩下的操作
for(int k=2;k<=m;++k){ //啊第m行也是要操作的 我好蠢。
for(int j=1;j<=n;++j){
if(a[k-1][j]){
flip(k,j);
vis[k][j]=1;
++cnt;
}
}
}
//判断最后一行状态
if(check()){
flag=1;
if(cnt<minn) {
minn=cnt;
memcpy(ans,vis,sizeof vis);
}else if(cnt==minn){
bool f=1;
for(int i=1;i<=m&&f;++i){
for(int j=1;j<=n&&f;++j){
if(vis[i][j]<ans[i][j]){
memcpy(ans,vis,sizeof vis);
f=0;
}else if(vis[i][j]>a[i][j]) f=0;
}
}
}
}
memcpy(a,b,sizeof b);
memset(vis,0,sizeof vis);
}
if(flag){
for(int i=1;i<=m;++i){
for(int j=1;j<=n;++j){
if(j!=1) cout<<" ";
cout<<ans[i][j];
}
puts("");
}
}else puts("IMPOSSIBLE");
return 0;
}
PS:同类型题目