题目概述
给定一4*4棋盘,每个位置有棋子,棋子黑面或白面朝上,每次可选一棋子翻转,其上下左右一格的棋子同时翻转,问可否使所有棋子同色朝上,若可,求最少需选几个棋子
时限
1000ms/3000ms
输入
共4行,每行4个字符,b代表黑面朝上,w代表白面朝上
限制
没有限制
输出
若可以,输出一数,最少选定棋子数,否则输出字符串Impossible
样例输入
case 1:
bwwb
bbwb
bwwb
bwww
case 2:
wwww
wwww
wwww
wwww
case 3:
wwww
wwww
wwww
wwwb
case 4:
wbww
bbbw
wbww
wwww
样例输出
case 1:
4
case 2:
0
case 3:
Impossible
case 4:
1
讨论
广搜,而且需要二进制及其操作,赋予电脑整体思考的能力,将棋盘上每个位置视为一个二进制位,如此一共16位,一个short正好,不过为求方便,实际使用的是unsigned short,因为不会牵扯符号问题且两个边界值很好表示,0和USHRT_MAX,采用广搜以求最少次数,每次搜索将上一次的值记录下,便于最后链式统计次数
如果棋盘再大一些咋办?那就得用bitset了把,比直接用二进制差不少呢
题解状态
292K,0MS,C++,1093B
题解代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 16
#define memset0(a) memset(a,0,sizeof(a))
#define EPS 1e-6
unsigned short B, marked[65536], opt[16] = { 0xc800,0xe400,0x7200,0x3100,0x8c80,0x4e40,0x2720,0x1310,0x08c8,0x04e4,0x0272,0x0131,0x008c,0x004e,0x0027,0x0013 };//Bytes 以双字节存放棋盘原始数据 marked 存放广搜时上一次的B值 operator 棋盘上可行的16种翻转操作 以十六进制表示 用win7的计算器可以很方便算出
queue<unsigned short>q;//广搜辅助队列
int bfs(unsigned short B)//广搜 返回值是次数 不可能返回-1
{
q.push(B);
while (!q.empty()) {
B = q.front();
q.pop();
if (!B || B == USHRT_MAX) {//分别代表全白和全黑
int times = 0;
while (marked[B] != USHRT_MAX) {//直到遇到起点标志为止
times++;
B = marked[B];//链式倒推会起点 也可以顺便把每一步记录下来
}
return times;
}
for (int p = 0; p < 16; p++)
if (!marked[B^opt[p]]) {//由于是广搜 因而先到者必然比后到者次数少 因而忽略后到者 异或操作表示翻转
marked[B^opt[p]] = B;//记录来源状态
q.push(B^opt[p]);
}
}
return -1;//所有状态都已搜索过 无法完成
}
int main(void)
{
//freopen("vs_cin.txt", "r", stdin);
//freopen("vs_cout.txt", "w", stdout);
for (int p = 15; p >= 0; p--) {
char c;
scanf("%c", &c);//input
if (p % 4 == 0)
getchar();//吞掉换行
if (c == 'b')
B |= 1 << p;//以或和左移操作置位 棋盘左上角为高位
}
marked[B] = USHRT_MAX;//代表全黑的情况 但由于不会有哪个状态由终点衍生 因而作为起点标志
int ans = bfs(B);
if (ans == -1)
printf("Impossible\n");//output
else
printf("%d\n", ans);//output
}
EOF