【题目描述】
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
Line 1: Two space-separated integers: M and N
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
Lines 1… M: Each line contains N space-separated integers, each specifying how many times to flip that particular location.
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
【题目分析】
看完题目后很明显应该是一个暴力搜索,但是应该从哪里开始搜索呢?从头开始的话会有215*15种可能,显然是不允许的。我们应该优化一下搜索的顺序,即哪些情况是显然不可能成立的。
我们如果从上往下搜索,那么下面的对上面的影响只有四个方向中上面的那个,那么如果搜索在x行,那么x-1行中的1下面的那块必须翻转,0下面的那块不能翻转。这样类推,我们发现除了第一行其他行的翻转情况都已经被确定,因此我们只需要枚举第一行的翻转情况(215种)就可以了。
看大佬的博客发现用到一种二进制的优化。如果没有这种优化的话我们可能需要搜索,相对比较复杂。
我们可以将一行中翻转的情况看做一个数,例如:110就是翻转前两个,那么总共的情况就是1<<m种
对于一个数字,我们如何快速判断哪些位是1呢?
我们可以用110与100,010,001的与判断,如果与的结果是0则该位是0,如果结果不是0则该位是1
有上面的方法后我们就可以写出程序啦
【AC代码】(借鉴大佬)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<climits>
#include<string>
#include<queue>
using namespace std;int n,m,num,cnt,ans;
const int MAXN=20;
int a[MAXN][MAXN]; //原数组
int b[MAXN][MAXN]; //用来修改的临时数组
int c[MAXN][MAXN]; //保存答案
const int drc[5][2]={0,0,0,1,0,-1,1,0,-1,0};void rev(int i,int j)
{int u,v;++cnt; c[i][j]=1; //记录答案for(int k=0;k<5;k++){u=i+drc[k][0]; v=j+drc[k][1];if(u<0 || u>=n || v<0 || v>=m) continue;b[u][v]^=1; //翻转}
}bool check(int x)
{cnt=0;memcpy(b,a,sizeof(b)); //直接将a拷贝给bfor(int i=0;i<m;i++){if(x&(1<<(m-1-i)))//1<<(m-1-i)是第i位为1其他位为0的数,用来判断x在第i位是不是1rev(0,i); //如果是1就翻转}for(int i=1;i<n;i++){for(int j=0;j<m;j++){if(b[i-1][j]) rev(i,j); //如果上一行是1就翻转}}for(int i=0;i<m;i++){if(b[n-1][i]) return false; //前面肯定都是0了,只需要判断最后一行是不是0就可以了}return true;
}int main()
{scanf("%d%d",&n,&m);ans=INT_MAX; num=-1;memset(a,0,sizeof(a));for(int i=0;i<n;i++){for(int j=0;j<m;j++){scanf("%d",&a[i][j]);}}for(int i=0,j=1<<m;i<j;i++){if(check(i) && cnt<ans) //这样记录的答案肯定是字典序,前面的能不翻就不翻,尽量翻后面的{ans=cnt; num=i;}}memset(c,0,sizeof(c)); //用来保存答案,之前都不用保存if(num!=-1){check(num);for(int i=0;i<n;i++){for(int j=0;j<m-1;j++){printf("%d ",c[i][j]);}printf("%d\n",c[i][m-1]);}}else{printf("IMPOSSIBLE");}return 0;
}