题目链接:http://poj.org/problem?id=3279
题目大意:给定n*m的格子,每个格子都有黑白两面(0表示白色,1表示黑色)。我们需要把所有的格子都反转成白色,每反转一个格子,它上下左右的格子都会跟着反转。请求出用最小步数完成反转时每个格子反转的次数。有多个解时,输出字典序最小的一组。
Description
Farmer John knows that an intellectually satisfied cow is a happy cow who will give more milk. He has arranged a brainy activity for cows in which they manipulate an M × N grid (1 ≤ M ≤ 15; 1 ≤ N ≤ 15) of square tiles, each of which is colored black on one side and white on the other side.
As one would guess, when a single white tile is flipped, it changes to black; when a single black tile is flipped, it changes to white. The cows are rewarded when they flip the tiles so that each tile has the white side face up. However, the cows have rather large hooves and when they try to flip a certain tile, they also flip all the adjacent tiles (tiles that share a full edge with the flipped tile). Since the flips are tiring, the cows want to minimize the number of flips they have to make.
Help the cows determine the minimum number of flips required, and the locations to flip to achieve that minimum. If there are multiple ways to achieve the task with the minimum amount of flips, return the one with the least lexicographical ordering in the output when considered as a string. If the task is impossible, print one line with the word "IMPOSSIBLE".
Input
Lines 2.. M+1: Line i+1 describes the colors (left to right) of row i of the grid with N space-separated integers which are 1 for black and 0 for white
Output
Sample Input
4 4 1 0 0 1 0 1 1 0 0 1 1 0 1 0 0 1
Sample Output
0 0 0 0 1 0 0 1 1 0 0 1 0 0 0 0
Source
题目思路:最开始拿到这个题,哇,真的不知道应该咋搜索,想了很久都没想法,菜得嫌弃自己,去网上看大牛写的题解,突破口在于翻两次和不翻结果一样,要求步数最小,那么我们当然选择不翻,这样其实如果上一行的位置是1,那么下一行的x位置必须翻转,否则不需要翻转。这样我们就只用枚举第一行的所有情况(感觉也就这里用了一点点搜索,最主要是要有解题想法),复杂度2^m,然后判断最后是否全为0来判断是否可能成功。
代码:
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
using namespace std;
#define FOU(i,x,y) for(int i=x;i<=y;i++)
#define FOD(i,x,y) for(int i=x;i>=y;i--)
#define MEM(a,val) memset(a,val,sizeof(a))
#define PI acos(-1.0)
const double EXP = 1e-9;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const ll MINF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const int N = 1e6+5;
int n,m;
int mp[20][20];
int mapp[20][20]; //翻转数组
int ans[20][20]; //记录结果
int tmp[20][20]; //记录中间值
int minn; //记录最小步数
int dir[4][2]={1,0,-1,0,0,1,0,-1};
void Copy(const int a[][20],int b[][20])
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
b[i][j]=a[i][j];
}
void change(int x,int y)
{
//翻转x,y这个位置
if(mapp[x][y]==1)
mapp[x][y]=0;
else
mapp[x][y]=1;
//翻转x,y周围位置
for(int i=0;i<4;i++)
{
int next_x=x+dir[i][0];
int next_y=y+dir[i][1];
if(mapp[next_x][next_y]==1)
mapp[next_x][next_y]=0;
else
mapp[next_x][next_y]=1;
}
}
void solve()
{
//这里要把第二行以后的tmp全部置为0,因为没翻转嘛
//不初始化的话会保存上次结果,因为这个wa了三发 -.-
for(int i=2;i<=n;i++)
for(int j=1;j<=m;j++)
tmp[i][j]=0;
int cnt=0; //记录翻转次数
Copy(mp,mapp); //复制一下mp数组给mapp用来翻转
for(int i=1;i<=m;i++) //判断第一行有几个1,翻转一下该位置旁边的
{
if(tmp[1][i]==1)
{
cnt++;
change(1,i); //翻转(i,j)这个位置及周围
}
}
//现在翻转第一行一下的,如果上一下的x位置是1,那么下一行的x位置必需翻转
//最后判断最后一行是否全0,全0说明满足要求
for(int i=2;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(mapp[i-1][j]==1)
{
cnt++;
tmp[i][j]=1; //该位置需要翻转
change(i,j);
}
}
}
bool flag=true;
for(int i=1;i<=m;i++)
{
if(mapp[n][i]!=0)
{
flag=false;
break;
}
}
if(flag)
{
if(cnt<minn)
{
minn=cnt;
Copy(tmp,ans);
}
}
}
void dfs(int x) //枚举第一行的1
{
if(x==m+1)
{
solve();
return ;
}
else
{
tmp[1][x]=0;
dfs(x+1);
tmp[1][x]=1;
dfs(x+1);
}
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
//std::ios::sync_with_stdio(false);
while(~scanf("%d%d",&n,&m))
{
MEM(mp,0);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&mp[i][j]);
minn=INF;
dfs(1);
//printf("minn=%d\n",minn);
if(minn==INF)
printf("IMPOSSIBLE\n");
else
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
printf("%d%c",ans[i][j],(j==m)?'\n':' ');
}
}
return 0;
}