POJ-3279___Fliptile —— 搜索

题目链接:点我啊╭(╯^╰)╮

题目大意:

     m × n m×n m×n的图, 1 1 1为开灯, 0 0 0为关灯,每次选择翻转图中的一点时,它包括相邻的四个点都要翻转,问能否能否全为 0 0 0,并输出全为 0 0 0时,翻转次数最少 且 字典序最小的方案???

解题思路:

    和一般的搜索不同,DFS和BFS都不行,一开始也许感觉无从下口,但根据它要求字典序最小,可以联想到状态压缩,当然这题显然不是状压,而是枚举。并且有一个前提,每一个点最多只能翻一次,两次则会重复。
    我们枚举搜索,怎么搜索呢、?只能从第一行开始搜索,假设我们将第一行已经确定了,那么从第二行开始翻转时,必须满足上一行对应的点为 1 1 1才能翻转(一行确定的意思并不代表上一行全为 0 0 0),这样只要确认了第一行,第二行也会被确定,以此类推…

代码思路:

    状态枚举第一行,逐个进行判断,记录翻转次数最小并且字典序最小的方案~

核心:灵活的理解搜索这思想,用枚举的方法来解决,最重要的是运用 确定了上一个状态,则解决了下一个状态的思想!!

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int m, n, fnum, num=INT_MAX;
int mp[20][20], a[20][20], f[20][20], ans[20][20];

void flip(int x, int y) {	//翻转 
	fnum++;
	f[x][y] = 1;
	a[x][y] = 1-a[x][y];
	a[x-1][y] = 1-a[x-1][y];
	a[x+1][y] = 1-a[x+1][y];
	a[x][y-1] = 1-a[x][y-1];
	a[x][y+1] = 1-a[x][y+1];
}

int main() {
	scanf("%d%d", &m, &n);
	for(int i=1; i<=m; i++)
		for(int j=1; j<=n; j++)
			scanf("%d", &mp[i][j]);

	for(int t=0; t<(1<<n); t++) {	//第一行所有情况 
		for(int i=1; i<=m; i++)
			for(int j=1; j<=n; j++)
				a[i][j] = mp[i][j];
		fnum = 0;
		int move = t;
		memset(f, 0, sizeof(f)); 
		for(int i=1; i<=n; i++) {	//翻转第一行 
			if(move&1) flip(1, i);
			move>>=1;
		}
		for(int i=2; i<=m; i++)		//翻转后面 
			for(int j=1; j<=n; j++)
				if(a[i-1][j]) flip(i,j);
		int i;
		for(i=1; i<=n; i++)	//判断最后一行 
			if(a[m][i]) break;

		if(fnum<num && i==n+1) {	//记录 
			num = fnum;
			for(i=1; i<=m; i++)
				for(int j=1; j<=n; j++)
					ans[i][j] = f[i][j];
		}
	}

	if(num==INT_MAX) printf("IMPOSSIBLE\n");
	else {
		for(int i=1; i<=m; i++) {
			for(int j=1; j<=n; j++)
				if(j==1) printf("%d", ans[i][j]);
				else printf(" %d", ans[i][j]);
			printf("\n");
		}
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值