POJ3279搜索

题意:

有一个n*m的格子,每个格子都有黑白两面(0表示白色,1表示黑色)。我们需要把所有的格子都反转成黑色,每反转一个格子,它上下左右的格子都会跟着反转。请求出用最小步数完成反转时每个格子反转的次数。有多个解时,输出字典序最小的一组。

思路:

**枚举第一行的所有翻转策略,逐个测试是否可行。**每个翻转策略是一个01串,比如010001表示第2位和第6位翻转,一共有m位,所以第一行的翻转策略数一共2^m个。
然后从第二行起,从上到下搜索,当前行是否需要反转取决于上一行的状态,通过翻转当前行使上一行为0,而不是通过上一行翻转为0后,看当前行的状态判断自己是否需要翻转,否则还会继续影响上一行。当前行是否需要反转取决于上一行的是否为黑色.
保证字典序最小:从小到大枚举就保证了字典序。
参考:https://www.cnblogs.com/caitian/p/5396946.html

#include 
#include 
#include 
#define first fi
#define second se
#define pii pair
using namespace std;
typedef long long LL;
const int maxn = 20;

int move[5][2] = {{0,1}, {1,0}, {0,-1}, {-1,0}, {0,0}};
int G[maxn][maxn];
int flip[maxn][maxn], opt[maxn][maxn];
int n,m;

// (i,j)位置的颜色.1-black,0-white. 
int color(int i, int j){
	int c = G[i][j];
	for(int k = 0; k < 5; ++k){
		int x = i + move[k][0];
		int y = j + move[k][1];
		if(x >= 0&&x < n&&y >= 0&&y < m) c+= flip[x][y];
	}
	return c&1;
}

int try_this(){
	for(int i = 1; i < n; ++i){ //从第二行开始 
		for(int j = 0; j < m; ++j){
			if(color(i-1,j) != 0) flip[i][j] = 1; //如果上一行G[i-1][j]是黑色,必则须翻转(i,j) 
		}
	}
	for(int i = 0; i < m; ++i){
		if(color(n-1,i) != 0) return -1; //判断最后一行是否全白,否则这个方案不可行 
	}
	int cnt = 0; // 统计翻转次数 
	for(int i = 0; i < n; ++i){
		for(int j = 0; j < m; ++j){
			cnt+= flip[i][j];
		}
	}
	return cnt;
}

int solve(){
	//枚举第一行的翻转 
	int ans = -1; 
	for(int i = 0; i < (1<>k)&1;
		}
		int num = try_this();
		if(num >= 0&&(ans < 0||ans > num)){
			ans = num;
			memcpy(opt, flip, sizeof(flip));
		} 
	}
	return ans;
}

int main()
{
	freopen("in.txt","r",stdin);
	while(scanf("%d %d",&n,&m) == 2){
		for(int i = 0; i < n; ++i){
			for(int j = 0; j < m; ++j)
			scanf("%d",&G[i][j]);
		}
		int ans = solve();
		if(ans == -1) printf("IMPOSSIBLE\n");
		else {
			for(int i = 0; i < n; ++i){
				for(int j = 0; j < m; ++j){
					printf("%d",opt[i][j]);
					if(j < m-1) printf(" ");
				}
				printf("\n");
			}
		}
	}

	return 0;
}


你可能感兴趣的:(搜索)