poj_3279 Fliptile

题目链接:poj_3279 Fliptile

在这里插入图片描述

题目大意:

  1. 01矩阵,改变其中一个,周围上下左右都改变(0->1 ,1->0),目的是让这个矩阵全都变为0
  2. 求解改变次数最小的那个,如果最小次数有多种情况,输出字典序列最小的情况

解题思路:

  1. 从第一行开始枚举,直到最后一行
  2. 如果最后一行不全为0,那么就无解
  3. 考虑翻转奇数次则会改变状态:
    奇数次为自身+上、左和右四个状态相加为奇数(自身状态此时还为1),则需要下一行来辅助变换

0 + 上、左、右 = 奇数, 那么此时0改变为1
1 + 上、左、右= 奇数,那么此时1还是1

代码如下:

状态压缩 + DFS
#include<iostream>
#include<cstring>
#include<queue>

using namespace std;

#define inf 0x3f3f3f3f
#define mm(a,x) memset(a,x,sizeof a)
#define ll long long

const int N = 30;

int n,m;
int g[N][N];
int tmp[N][N];
int ans[N][N];
int res;
int dx[4] = {0,-1,0,0};
int dy[4] = {0,0,-1,1};

int dfs() {
	for(int i = 0; i < n - 1; i ++ ) {
		for(int j = 0; j < m; j ++ ) {
			int p = g[i][j];
			//枚举四种情况:自身、上、左、右 
			for(int k = 0; k < 4; k ++ ) {
				int a = i + dx[k];
				int b = j + dy[k];
				if(a < 0 || a > n - 2 || b < 0 || b > m - 1) continue;
				p += tmp[a][b];
			}
			//如果为奇数 
			if(p & 1) {
				tmp[i + 1][j] = 1;
			}
		}
	}
	int i = n - 1;
	for(int j = 0; j < m; j ++ ) {
		int p = g[i][j];
		for(int k = 0; k < 4; k ++ ) {
			int a = i + dx[k];
			int b = j + dy[k];
			if(a < 0 || a > n - 1 || b < 0 || b > m - 1) continue;
			p += tmp[a][b];
		}
		if(p & 1) {
			return -1;
		}
	}
	int t = 0;
	for(int i = 0; i < n; i ++ ) {
		for(int j = 0; j < m; j ++ ) {
			if(tmp[i][j] & 1) t ++;
		}
	}
	return t;
}
int main() {
	cin >> n >> m;
	for(int i = 0; i < n; i ++ ) {
		for(int j = 0; j < m; j ++ ) {
			cin >> g[i][j];
		}
	}
	res = inf;
	for(int i = 0; i < (1 << n); i ++ ) {
		mm(tmp,0);
		//第一行 
		for(int j = 0; j < m; j ++ ) {
			tmp[0][j] = (i >> j) & 1;
		}
		int t = dfs();
		if(t == -1) continue;
		//更新最优解 
		if(res > t) {
			res = t;
			for(int i = 0; i < n; i ++ ) {
				for(int j = 0; j < m; j ++ ) {
					ans[i][j] = tmp[i][j];
				}
			}
		}
	}
	//如果无解 
	if(res == inf) {
		cout<<"IMPOSSIBLE\n";
	} else {
		for(int i = 0; i < n; i ++ ) {
			for(int j = 0; j < m; j ++ ) {
				if(j) cout<<" ";
				cout<<ans[i][j];
			}
			puts("");
		}
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值