poj-3279 Fliptile

题目地址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
#include
#include

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;
}

你可能感兴趣的:(算法,算法)