poj 3279 Fliptile dfs 反转

传送门 

这道题和开关灯模拟差不多 参考:https://blog.csdn.net/to_be_better/article/details/49901079

题目大意
给一个N行M列的矩阵,值分别为0和1,每次你可以选择将一个变成相反状态,同时,它周围的四个数也会变为相反状态。 
问:最少翻转多少次,可以将所有值都变成0 
多个解,输出翻转次数最少的(若有次数相同解,输出字典序小的) 
若无解,输出”IMPOSSIBLE”
思路
对于每个点,只能有两种操作,翻或不翻,若暴力所有可能性,需要2^(M*N)次操作,显然不可行 
所以有了这个法子。 
先枚举第一行的所有可能性(2^M),搜索或位运算均可 
然后,对坐标(i, j)来说,如果(i-1, j)不为0,那么(i, j)必然需要翻转。 
重复进行上操作由2至N 
此时,最后一行也已翻转完毕,如果最后一行全为0,得出结果 
第一行的所有结果中取最小值


代码:

#include <stdio.h>
#include <iostream>
#include <vector>
#include <math.h>
#include <algorithm>
#include <queue>
#include <string.h>
using namespace std;
int g[50][50];
int f[50][50];
int ans[50][50];
int minn=0x3f3f3f3f;
int n,m;
int check()
{
	for(int i=1;i<=m;i++)//判断最后一行是否能全都变为0 
	{
		int t=f[n][i]+f[n][i-1]+f[n][i+1]+f[n-1][i];
		if((g[n][i]+t)%2)
		{
			return 0;
		}
	}
	return 1;
}
void dfs(int k,int num)
{
	if(num>minn)
	{
		return ;// 剪侄 
	}
	if(k>n)
	{
		if(check()&&minn>num)
		{
			memcpy(ans,f,sizeof(ans));
			minn=num;
		}
		return ;
	}
	int t=0;
	for(int i=1;i<=m;i++)
	{
		if((g[k-1][i]+f[k-2][i]+f[k-1][i-1]+f[k-1][i+1]+f[k-1][i])%2)// 判断上一行的四周是否都为0 
		{
			f[k][i]=1;
			t++;
		}
		else
		{
			f[k][i]=0;
		}
	}
	dfs(k+1,num+t);
} 
void init(int k,int num)
{
	if(k>m)
	{
		dfs(2,num);
		return ;
	}
	f[1][k]=0;// 不变
	init(k+1,num);
	f[1][k]=1;// 改变
	init(k+1,num+1); 
}
int main()
{
	cin>>n>>m;
	memset(f,0,sizeof(f));
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			cin>>g[i][j];
		}
	}
	init(1,0);
	if(minn==0x3f3f3f3f)
	{
		cout<<"IMPOSSIBLE"<<endl;
	}
	else
	{
		for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
                cout<<ans[i][j]<<" ";
            cout<<endl;
        }
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/henucm/article/details/88368088
今日推荐