poj3279 Fliptile

原题链接:poj3279 Fliptile

题意:可以看作一个M*N的棋盘,棋盘中布满黑白色的棋子,黑色翻转后是白色,白色反转是黑色,每步操作翻转一个棋子,并且其上下左右也都跟着翻转。

求:尽可能少的步数把所有的棋子翻成白色,最小步数有多个时,输出字典序最小的一组,无解时输出IMPOSSIBLE

输入:M N  棋子颜色(0代表白色,1代表黑色)

输出:每个棋子对应的最小步数

#include <cstdio>
#include <cstring>
using namespace std;
const int MAX_N = 16;
const int MAX_M = 16;
const int dex[2][5] = {	{-1, 0, 0, 0, 1}, 
						{0, -1, 0, 1, 0}};
//输入数据 
int M, N;
int tile[MAX_M][MAX_N];

int opt[MAX_M][MAX_N]; 	//保存最优解 
int flip[MAX_M][MAX_N];	//保存中间值 
int get(int x, int y){	//查询(x,y)的颜色 
	int cnt = tile[x][y];
	for(int i = 0;i < 5;i ++){
		int dx = x + dex[0][i], dy = y + dex[1][i];
		if(dx >= 0 && dx < M && dy >= 0 && dy < N){
			cnt += flip[dx][dy];
		}
	}
	return cnt % 2;
}
int calc(){	//求出第1行确定的情况下的最小操作次数,不存在是返回-1 
	int i, j;
	for(i = 1;i < M;i ++){	//从第2行开始翻转 
		for(j = 0;j < N;j ++){
			if(get(i - 1, j) != 0){	//上一行(i-1,j)如果是黑色,(i,j)需要翻转 
				flip[i][j] = 1;
			}
		}
	}
	for(j = 0;j < N;j ++){	//最后一行是否全白 
		if(get(M - 1, j) != 0){	//如果有黑色,则无解 
			return -1;
		}
	}
	int res = 0;	//统计翻转的次数 
	for(i = 0;i < M;i ++){
		for(j = 0;j < N;j ++){
			res += flip[i][j];
		}
	}
	return res;
}
int main(){
	int i, j;
	while(~scanf("%d%d", &M, &N)){
		for(i = 0;i < M;i ++){ 	//输入
			for(j = 0;j < N;j ++)
				scanf("%d", &tile[i][j]);
		}
		int ans = -1;
		for(i = 0;i < 1 << N;i ++){	//按照字典序尝试第一行的所有可能性 
			memset(flip, 0, sizeof(flip));
			for(j = 0;j < N;j ++){
				flip[0][N - j - 1] = i >> j & 1;
			}
			int num = calc();
			if(num >= 0 && (ans < 0 || num < ans)){
				ans = num;
				memcpy(opt, flip, sizeof(flip));
			}
		}
		if(ans < 0){	//-1时无解 
			printf("IMPOSSIBLE\n");
		}else{
			for(i = 0;i < M;i ++){
				for(j = 0;j < N;j ++)
					printf("%d%c", opt[i][j], j + 1 == N ? '\n' : ' ');
			}
		}
	}	
	return 0;
}
代码来源于:挑战程序设计竞赛(第2版)P154
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值