题目来源:http://poj.org/problem?id=3279
有一个m*n的棋盘,每个格子上是0或1,每次可以对一个格子做一次翻转操作,将翻动的格子和上下左右4个格子的0/1翻转。问最少做多少次翻转可以将所有格子翻转成0,输出翻转方案。没有方案时输出“IMPOSSIBLE”。
拿到题目还是没想到有效的解决办法,因为放在搜索里,所以往那靠。
先枚举第一行的所有状态(也就是翻转的状态)(并不是改变原来的第0行,而是改变第一行的book翻转,进行方便之后的判断),0~~1<<n,2的n次方的不同形式,然后往下走下面的行,直到m-1行,看看m行是不是都为0了。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
using namespace std;
int n,m;
int s[20][20],book[20][20],per[20][20];
int move[5][2]={{0,0},{0,1},{0,-1},{-1,0},{1,0}};
int check(int x,int y)//判断周围的块翻动的次数导致的结果
{
int temp=s[x][y];
for(int i=0;i<5;i++)
{
int xx=x+move[i][0];
int yy=y+move[i][1];
if(xx<0||yy<0||xx>=m||yy>=n)
continue;
temp+=book[xx][yy];
}
return temp%2;//奇数则为1,偶数为0
}
int dfs()
{
int i,j;
for(i=1;i<m;i++)
{
for(j=0;j<n;j++)
if(check(i-1,j))
book[i][j]=1;
}
for(i=0;i<n;i++)
if(check(m-1,i))
return -1;
int sum=0;
for(i=0;i<m;i++)
for(j=0;j<n;j++)
sum+=book[i][j];
return sum;
}
int main()
{
int i,j;
scanf("%d %d",&m,&n);
memset(book,0,sizeof(book));
for(i=0;i<m;i++)
{
for(j=0;j<n;j++)
{
scanf("%d",&s[i][j]);
}
}
int flag=0,ans=0x3f3f3f3f;
for(i=0;i<1<<n;i++)
{
memset(book,0,sizeof(book));
for(j=0;j<n;j++)
book[0][n-1-j]=i>>(j)&1;
int hh=dfs();
if(hh>=0&&hh<ans)
{
ans=hh;
flag=1;
memcpy(per,book,sizeof(book));
}
}
if(!flag)
printf("IMPOSSIBLE");
else
{
for(i=0;i<m;i++)
{
for(j=0;j<n;j++)
printf("%d ",per[i][j]);
printf("\n");
}
}
return 0;
}