题目地址https://vjudge.net/problem/POJ-3279
思路来源https://blog.csdn.net/to_be_better/article/details/49901079
##题目大意
给你一个n*m的矩阵,由0和1组成,你可以翻转其中任意一块,当你翻转时 它周围的与他有一条边相连的也会被翻转, 问: 怎么翻转才能使全部方块为0 输出翻转次数最小的方法 如果方法不存在 输出"IMPOSSIBLE"
##思路
首先对第一行进行枚举 把第一行所有可能的情况枚举一遍 然后针对下一行 如果上一个“块” 是1
那么翻转这个块 这样在第二行的时候 会把第一行全部翻转成0 依次下去 到最后一行的时候 判断最后一行是否全为0既可 当然针对每列也可以
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m;
int str[20][20];
int f[20][20];
int arr[20][20];
int minn=9999999;
bool judge() //判断最后一行是否全为0
{
for(int i=0;i<m;++i)
{
if((str[n-1][i]+f[n-1][i]+f[n-1][i-1]+f[n-2][i]+f[n-1][i+1])&1) //下面有段类似的注释
return false;
}
return true;
}
void flip(int a,int b) // 一个没用的flip
{
f[a][b]=!f[a][b];
if(a-1>=0) f[a-1][b]=!f[a-1][b];
if(a+1<n) f[a+1][b]=!f[a+1][b];
if(b-1>=0) f[a][b-1]=!f[a][b-1];
if(b+1<m) f[a][b+1]=!f[a][b+1];
}
void dfs(int k,int num)
{
if(minn<num) return;//剪枝 吧
if(k>n-1) //到最后一行了
{
if(judge() && minn>num) 判断最后一行是否全为0 并且是否是最小的情况
{
memcpy(arr,f,sizeof f);
minn=num; //更新最小值
}
return;
}
int t=0;
for(int i=0;i<m;++i)
{
if((str[k-1][i]+f[k-1][i]+f[k-1][i-1]+f[k-2][i]+f[k-1][i+1])&1)
/*判断上一个块是否为1 其中f数组表示是否翻转 那么上一个块的值由他上一个和他前一个和他后一个的翻转情况+他本身的值 和他本身的翻转情况 来决定 看了大佬的blog 想了两天..才看懂 此处用位运算来表示*/
{
f[k][i]=1;
t++;
}
else f[k][i]=0;
}
dfs(k+1,num+t);
}
void xxx(int k,int num)
{
if(k>m-1)
{
dfs(1,num); //每当枚举完一种情况 都搜索一下
return;
}
f[0][k]=0; //翻转的情况
xxx(k+1,num);
f[0][k]=1; 。//不翻转的情况
xxx(k+1,num+1);
}
int main()
{
while(cin >> n >> m)
{
memset(f,0,sizeof f);
memset(arr,0,sizeof arr);
for(int i=0;i<n;++i) //输入
for(int j=0;j<m;++j)
cin >> str[i][j];
xxx(0,0); //针对第一行进行枚举
if(minn==9999999) printf("IMPOSSIBLE\n");
else
for(int i=0;i<n;++i)
for(int j=0;j<m;++j)
printf("%d%c",arr[i][j],j==m-1?'\n':' ');
}
return 0;
}