poj3279Fliptile
题目大意及理解
大意就是心情愉悦能让奶牛产更多的奶,所以农民搞了个游戏给奶牛玩,奶牛
需要把所有黑色朝上的格子变成白色(1代表黑色,0代表白色),因为奶牛的
力气比较大,所以在翻动某一个格子时会同时翻转这个格子的邻接格子(上,下
左, 右)问怎样翻动可以使奶牛所需翻动次数最少,把每个格子的翻动次数以
矩阵的形式输出(只输出最少的)。如果全白时做不到的,输出IMPOSSIBLE
M-行数,N-列数
因为当第一行的翻动次数确定了,第一行的颜色也就确定了,所以第二行只需要
翻动本行格子上面的格子为黑色的格子,就能保证上一行全白。最后可以保证
M-1行全白,只需检验最后一行是否全白,就可以判断该种情况是否可行。
所以这到题目的重点是:
1.二进制枚举 第一行每个格子翻动状态只有翻或者不翻(可以想想为什么)
如果用0,1来表示,那么第一行所有的可能情况就是0 到 2^N所有数的二进制
表示。枚举所有情况即可。
2.搜索边界判断 可能这就是这道题目被归类到搜索题的原因。用搜索边界判
断的方法很好的解决了对于处在图不同地方的格子,对这些格子的颜色判断的
公式不同导致的繁琐。减少了代码的复杂性。
AC代码
# include <stdio.h>
# include <string.h>
int M, N;
int Map_f[20][20];
int Map_n[20][20];
int Map_ans[20][20];
int cnt = 0x3f3f3f3f;
int dir[5][2] = {
-1,0,
0,0,
0,-1,
1,0,
0,1
};
int num;
int getcolor(int x, int y){
int Count = 0;
for(int i = 0; i < 5; i++) {
int xx = x + dir[i][0];
int yy = y + dir[i][1];
//搜索边界判断
if(xx < 0 || yy < 0 || xx >= M || yy >= N) continue;
Count += Map_n[xx][yy];
}
Count += Map_f[x][y];
if(Count%2)
return true;
else
return false;
}
bool judge() {
num = 0;
for(int i = 0; i < N; i++) {
if(Map_n[0][i] == 1) num++;
}
for(int i = 1; i < M; i++) {
for(int j = 0; j < N; j++) {
if(getcolor(i-1,j)) {
Map_n[i][j] = 1;
num++;
}
else
Map_n[i][j] = 0;
}
}
for(int i = 0; i < N; i++) {
if(getcolor(M-1,i))
return false;
}
return true;
}
int main() {
scanf("%d%d",&M,&N);
for(int i = 0; i < M; i++) {
for(int j = 0; j < N; j++) {
scanf("%d",&Map_f[i][j]);
}
}
/*for(int i = 0; i < M; i++) {
for(int j = 0; j < N; j++) {
printf("%d ",Map_f[i][j]);
}
printf("\n");
}*/
for(int k = 0; k < 1<<N; k++) {//二进制枚举。
memset(Map_n,0,sizeof(Map_n));
for(int i = 0; i < N; i++) {
Map_n[0][i] = k>>i&1;
/*
先把需要比较的某位数字移动到个位,再和1进行按位与计算
能保证,结果为0或者1
*/
}
if(judge() && num < cnt) {
for(int i = 0; i < M; i++) {
for(int j = 0; j < N; j++) {
Map_ans[i][j] = Map_n[i][j];
}
}
cnt = num;
}
}
if(cnt == 0x3f3f3f3f)
printf("IMPOSSIBLE\n");
else {
for(int i = 0; i < M; i++) {
for(int j = 0; j < N; j++) {
printf("%d",Map_ans[i][j]);
if(j != N-1)
printf(" ");
}
printf("\n");
}
}
return 0;
}