POJ 3279 Fliptile 开关问题

传送门:POJ3279

题意:牛翻地,每翻动一块地其上下左右的四块地也会跟着翻转,问最怎样用最少的步数把地都翻成正面向上。(1代表反面,0代表正面)并且输出要是字典序最小。

思路:典型开关问题,不过是二维的,建议先做POJ3276的一维开关问题,至于开关问题的理论推导,特别推荐看挑战程序设计P150-P157,尤其是最后的集合整数的讲解表示个人感觉极为精妙。

然后就题论题,本题主要思路就是先枚举第一行的状态,第一行的状态确定了,那么如果第一行有反面向上的,只能通过翻转第二行去改变,如此第二行的翻转情况就被确定了,以此类推直到最后一行,然后检查最后一行的状态,看看最后一行是否全部正面朝上来判断此解是否可行。

重点:1.同一个格子翻两次就会恢复原状

   2.翻地的集合确定了,翻的先后顺序是无关紧要的

   3.要做到输出是字典序最小,就要在枚举第一行状态的时候按照字典序枚举。

PS:此题最快解法应该是高斯消元,不过蒟蒻还没学。。等到学了以后再掏出来练手吧。

代码:

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<stack>
#include<set>
#include<vector>
#include<map>
#define ll long long
#define pi acos(-1)
#define inf 99999999
using namespace std;
typedef pair<int,int>P;
int m,n;
int mp[20][20],flip[20][20],ans[20][20];//原数组、临时数组、结果数组
int go[5][2]={0,0,1,0,0,1,-1,0,0,-1};
int getcolor(int i,int j)//判断某块地是正面还是反面
{
	int sum=mp[i][j];
	for(int k=0;k<5;k++)
	{
		int di=i+go[k][0];
		int dj=j+go[k][1];
		if(0<=di&&di<m&&0<=dj&&dj<n)
		sum+=flip[di][dj];
	}
	return sum&1;
}
int check()//确定第一行的状态下判断是否有可行解
{
	for(int j=1;j<m;j++)
	{
		for(int k=0;k<n;k++)
		{
			flip[j][k]=getcolor(j-1,k);
		}
	}
	for(int i=0;i<n;i++)
	if(getcolor(m-1,i))
	return -1;
	int sum=0;
	for(int i=0;i<m;i++)
	for(int j=0;j<n;j++)
	sum+=flip[i][j];
	return sum;
}
void solve()
{
	int res=inf;
	for(int i=0;i<1<<n;i++)//枚举第一行状态
	{
		memset(flip,0,sizeof(flip));
		for(int j=0;j<n;j++)
		{
			flip[0][n-1-j]=(i>>j)&1;//集合的整数表示
		}
		int t=check();
		 if(t>=0&&res>t)
		 {
		 	res=t;
		 	memcpy(ans,flip,sizeof(flip));
		 }
	}
	if(res==inf)
	printf("IMPOSSIBLE\n");
	else
	{
		for(int i=0;i<m;i++)
		for(int j=0;j<n;j++)
		printf("%d%c",ans[i][j]," \n"[j==n-1]);
	}
}
int main()
{
	while(~scanf("%d%d",&m,&n))
	{
		for(int i=0;i<m;i++)
		for(int j=0;j<n;j++)
		scanf("%d",&mp[i][j]);
		solve();
	}
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值