Fliptile (POJ

给你一个01矩阵,矩阵大小为M x N。(1 <= M , N <= 15)
每次操作选择一个格子,使得该格子与上下左右四个格子的值翻转。
至少多少次操作可以使得矩阵中所有的值变为0?
请输出翻转方案,若没有方案,输出"IMPOSSIBLE” 。

Input
第一行输入两个数:M和N。(1 <= M , N <= 15)
接下来M行,每行N个数,其值只为0或1。

Output
输出M行,每行N个数。
每个数代表该位置翻转次数

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

刚看题毫无头绪,既要反转次数最少又要字典序最小,暴力枚举复杂度得2^(m*n),太大了,状压也不知道该怎么做。
仔细观察可以发现,要想使矩阵全部为0,那么每个1都要反转,而且每个位置最多反转一次,如果反转两次就又回去了做的是无用功。既然每个1都要反转,那么对于[i,j]这个位置,如果[i-1,j]为1的话,[i,j]就必须要反转,所以上一行的状态确定了,这一行必须反转的位置就也确定了,所以第1行的状态确定了,第2行就确定了,那第3行业确定了…,那么总的反转次数就确定了。
所以首先要确定第一行的状态(第一行不一定全为0,因为第二行的时候会把第一行全变为0),这样到第最后一行的时候前面全部变为0了,但最后一行不一定,判断一下即可。
还有一个条件是反转的字典序最小,因为第一行状态确定,后面所有行的饭庄位置就确定了,所以第一行的反转状态字典序最小整个的字典序就最小了,所以从小到大枚举第一行的反转状态,取反转次数最小的那个。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=100;
int n,m,a[N][N],cnt[N][N],num[N][N]; //cnt保存当前状态的反转矩阵,num保存最终的答案矩阵
void reverse(int x,int y)//以x,y为中心反转
{
	a[x][y]=1-a[x][y];
	if(x>1) a[x-1][y]=1-a[x-1][y];
	if(y>1) a[x][y-1]=1-a[x][y-1];
	if(y<m) a[x][y+1]=1-a[x][y+1];
	if(x<n) a[x+1][y]=1-a[x+1][y];
}
int dfs(int x,int y)
{
	int t=0;//统计反转次数
	if((x==n+1))
	{
		bool flag=1;
		for(int j=1;j<=m;j++) if(a[n][j]) flag=0;
		if(cnt[x][y]) reverse(x,y);//将矩阵恢复原样
		if(flag) return t;
		else return 0x3f3f3f3f;//如果不可行返回0x3f3f3f3f这个状态的次数就一定不会比答案tmp小,就不会出错了
	}
	if(a[x-1][y])
	{
		reverse(x,y);
		t++;
		cnt[x][y]=1;
	}
	if(y<m) t+=dfs(x,y+1);
	else t+=dfs(x+1,1);
	if(cnt[x][y]) reverse(x,y);//恢复矩阵
	return t;
}
int tmp=0x3f3f3f3f;
void solve(int i,int t)//正向遍历第一行,第一次判断的反转状态是0000,然后递归回m,这是的状态是0001,再递归回去就是0010,然后0011...就实现了从小到大枚举第一行的状态
{
	if(i==m+1)
	{
		for(int j=2;j<=n;j++)//将第二行一下的cnt清空
			for(int k=1;k<=m;k++) cnt[j][k]=0;
		t+=dfs(2,1);//计算第2行以后的反转次数
		if(t<tmp)
		{
			for(int j=1;j<=n;j++)//保存反转矩阵
				for(int k=1;k<=m;k++)
					num[j][k]=cnt[j][k];
			tmp=t;
		}
	  	return ;
	}
	solve(i+1,t);
	reverse(1,i);
	cnt[1][i]=1;
	solve(i+1,t+1);
	reverse(1,i);//记得恢复现场
	cnt[1][i]=0;
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			cin>>a[i][j];
	solve(1,0);
	if(tmp<0x3f3f3f3f)
	{
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=m;j++)
				cout<<num[i][j]<<' ';
			puts("");
		}
	}
	else puts("IMPOSSIBLE");
}
原文地址:https://www.cnblogs.com/neflibata/p/12871748.html