POJ-3279___Fliptile——搜索

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

题目大意:

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

解题思路:

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

猜你喜欢

转载自blog.csdn.net/Scar_Halo/article/details/83033119