Fliptile(翻格子问题)

题目大意:

就是给定一个m*n的矩阵代表地板砖,0代表白砖,1代表黑砖,每个砖都有两面,一面白一面黑,翻转一次即可把白变成黑把黑变成白,但与此同时,与该砖块相邻的上下左右四块转也随之翻转。要求使用最少的翻转次数,将砖块翻转。 要求输出反转位置。

思路:

这是典型的开关问题,重点是要想到第一行的格子能被第一行翻转影响,也能被第二行反转影响,所以第一行要特殊处理,二进制枚举第一行所有翻转情况,之后每一行的反转就都有唯一解了。因为剩下的过程就是:用第二行翻转来保证第一行全白,用第三行翻转来保证第二行全白......依次类推,最后检查最后一行是否都是白色,如果是就得到一个结果,否则就无解。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string.h>
#include <climits>
using namespace std;
const int MAXN = 17;
//五个方向
const int dirx[5] = {0, 1, -1, 0, 0};
const int diry[5] = {0, 0, 0, 1, -1};

int m, n; //行数,列数
int board[MAXN][MAXN]; //初始情况
int tra[MAXN][MAXN]; //临时的翻转记录
int ans[MAXN][MAXN]; //最终结果(最终的翻转记录)

//查询(x, y)的颜色
int get(int x, int y){
	int c = board[x][y]; //初始颜色
	for(int i=0; i<5; i++){
		int xi = x + dirx[i];
		int yi = y + diry[i];
		if(xi>=0 && xi<m && yi>=0 && yi<n){
			//加上所有翻转情况
			c += tra[xi][yi];
		}
	}
	return c % 2; //返回最后得到的颜色
}

//求第1行确定的情况下的最少操作次数
//解不存在的话返回-1
int dfs(){
	//首先确定从第二行开始的翻转方法
	for(int i=1; i<m; i++){ //遍历行
		for(int j=0; j<n; j++){ //遍历列
			if(get(i-1, j) == 1){ //检查上一行是否为黑色,判断是否需要翻转
				//如果是1,需要翻转
				tra[i][j] = 1;
			}
		}
	}

	//然后检查最后一行是否都为白色,判断这种方法是否可行
	for(int j=0; j<n; j++){
		if(get(m-1, j) == 1) return -1; //只要有一个是黑色,就不可行
	}

	//能到达这里,说明该方法可行,那就统计翻转次数并返回
	int count = 0;
	for(int i=0; i<m; i++){
		for(int j=0; j<n; j++){
			count += tra[i][j];
		}
	}
	return count;
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);

	//输入
	cin >> m >> n;
	for(int i=0; i<m; i++){
		for(int j=0; j<n; j++){
			cin >> board[i][j];
		}
	}

	int res = -1;
	for(int i=0; i<(1<<m); i++){ //遍历第一行所有翻转情况
		memset(tra, 0, sizeof(tra));
		for(int j=0; j<n; j++){
			tra[0][j] = (i>>j) & 1; //记录第一行的翻转结果
		}
		int cur = dfs(); //计算当前情况的翻转次数
		if(cur >= 0 && (res == -1 || cur < res)){
			//遇到更优解,就重新记录
			res = cur;
			memcpy(ans, tra, sizeof(tra)); //把tra复制到ans中去
		}
	}

	if(res == -1) {cout << "IMPOSSIBLE" << endl; return 0;}
	else {
		for(int i=0; i<m; i++){
			for(int j=0; j<n; j++){
				printf("%d%c", ans[i][j], j+1 == n ? '\n' : ' ');
			}
		}
	}
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值