题意:给你一个4*4的棋盘,每个棋子非黑即白,翻一个棋子会带动它上下左右的棋子也翻过来,问至少多少次可以使棋盘全黑或者全白。
分析:因为是至少翻多少次,我们就可以联想到利用BFS来做,因为棋盘只有16位,我们就以一个16位二进制数来存储当前棋盘的状态。当前棋盘的状态就类似普通bfs中的某个节点。从当前节点扩展到下一节点的方法就是以16个格子为中心进行翻转。
代码:
#include <stdio.h>
#include <string.h>
#include <cmath>
#include <algorithm>
#include <iostream>
#include<vector>
#include<queue>
using namespace std;
int g[6][6];
int n = 4;
int dir[4][2] = { {1,0},{-1,0}, {0,1}, {0,-1} };
int state = 0;//初始状态
int vis[1 << 16];//标记状态是否被访问
int ans = 0; //最短步数
int target[2] = { 0,0xffff }; //0表示全黑,0xffff表示16位全1
bool check(int r, int c) {
if (r < 0 || r >= n || c < 0 || c >= n)
return false;
return true;
}
int flip(int state,int r,int c) { //翻转函数,传入当前格子状态和,以r,c为中心的坐标
int mask = 1 << (r * 4 + c);
state ^= mask; //翻转坐标r,c的格子
for (int i = 0; i < 4; i++) {
int x = r + dir[i][0];
int y = c + dir[i][1];
if (check(x, y)) { //判断上下左右是否合法
mask= 1 << (x * 4 + y);
state ^= mask;
}
}
return state;
}
int bfs(int state) {
int step = 0;
queue<int>q;
q.push(state);
vis[state] = 1;
while (!q.empty()) {
int size = q.size();
for (int i = 0; i < size; i++) {
int f = q.front();
q.pop();
if (f == target[0] || f == target[1]) //出队列的时候才是当前步数
return step;
for (int j = 0; j < 16; j++) {
int tmp = flip(f, j / 4, j % 4); //返回翻转后的状态
if (!vis[tmp]) {
q.push(tmp);
vis[tmp] = 1;
}
}
}
step++;
if (step > 16) //因为最多翻转16次,超过16次直接返回-1
return -1;
}
return -1;
}
int main()
{
char c;
for(int i=0;i<4;i++)
for (int j = 0; j < 4; j++) {
cin >> c;
if (c == 'b')
g[i][j] = 0;//黑为0
else {
g[i][j] = 1; //白为1
state += (1 << (i * 4 + j));
}
}
int ans=bfs(state);
if(ans==-1)
printf("Impossible\n");
else
printf("%d",ans);
return 0;
}
DFS解法:因为以每个格子翻转奇数次产生的效果和翻转偶数次产生的效果是一样的。所以每个格子只有两种选择,翻转一次或者不翻转,这样就可以利用DFS枚举所有的状态,返回最小值即可。解空间组成了一颗完全二叉树。
#include <stdio.h>
#include <string.h>
#include <cmath>
#include <algorithm>
#include <iostream>
#include<vector>
#include<queue>
using namespace std;
int g[6][6];
int n = 4;
int dir[4][2] = { {1,0},{-1,0}, {0,1}, {0,-1} };
int state = 0;//初始状态
int vis[1 << 16];//标记状态是否被访问
int ans = 17; //最短步数
int target[2] = { 0,0xffff };
bool check(int r, int c) {
if (r < 0 || r >= n || c < 0 || c >= n)
return false;
return true;
}
int flip(int state,int r,int c) { //传入state
int mask = 1 << (r * 4 + c);
state ^= mask; //翻转坐标r,c的格子
for (int i = 0; i < 4; i++) {
int x = r + dir[i][0];
int y = c + dir[i][1];
if (check(x, y)) { //判断上下左右是否合法
mask= 1 << (x * 4 + y);
state ^= mask;
}
}
return state;
}
void dfs(int idx, int state,int cnt) {
if (cnt >= ans) //如果当前答案已经小于cnt,直接返回
return;
if (idx == 16) { //最后一次特判一下,因为要直接return了
if (state == target[0] || state == target[1]) {
ans = min(ans, cnt);
}
return;
}
if (state == target[0] || state == target[1]) {
ans = min(ans, cnt);
return;
}
//翻转state
int tmp = flip(state, idx / 4, idx % 4);
dfs(idx + 1, tmp, cnt + 1);
//不翻转
dfs(idx + 1, state, cnt );
}
int main()
{
char c;
for(int i=0;i<4;i++)
for (int j = 0; j < 4; j++) {
cin >> c;
if (c == 'b')
g[i][j] = 0;//黑为0
else {
g[i][j] = 1; //白为1
state += (1 << (i * 4 + j));
}
}
dfs(0, state,0);
if (ans == 17)
printf("Impossible\n");
else
printf("%d", ans);
return 0;
}