Description
Input
Output
Sample Input
bwwb bbwb bwwb bwww
Sample Output
4
题意为有一个4*4的棋盘,由黑棋子和白棋子填满着(黑棋子用'b'表示,白棋子用'w'表示),我们所做的就是翻转棋子,黑色棋子经过一次翻转成为白色棋子,白色棋子经过一次翻转成为黑色棋子,翻转的规则为:每次随机选择一枚棋子,翻转它以及它周围的棋子(上下左右),问最少经过翻转几次,棋盘所呈现的状态为全黑或者全白。
提示:其实每格棋子最多只可以翻转一次(实际是奇数次,但这没意义),只要其中一格重复翻了2次(不论是连续翻动还是不连翻动),那么它以及周边的棋子和没翻动时的状态是一致的,由此就可以确定这个棋盘最多只能走16步,最多只能有翻出2^16种状态
我们用一个16位的二进制数来表示棋盘的一个状态,比如 1 1 1 1, 1 1 1 1, 1 1 1 1, 1 1 1 1(16个1)即数字65535。我们假设1代表的是黑棋子,那么65535这个状态表示为16个棋子全是黑色,符合题意要求;而0这个状态表示为棋子全是白色,也符合要求。
不同的数字代表着棋盘不同的状态比如 1 1 1 1, 1 1 0 1, 1 1 0 0, 1 1 0 0 代表着有5枚白棋子,11枚黑棋,且可以知道白棋所在的位置,即在第二行的第三个(二进制数从右到左第9位,位数从0开始数),第三行的第三第四个,第四行的第三第四个。即棋盘的状态为
b b b b
b b w b
b b w w
b b w w
我们把棋盘上的棋子从上到下,从左向右编号,即0,1,2,3~15,那么它的编号和它在一个二进制表示的状态中的位数(第几位)之间的关系为 label= 15- 编号。比如棋盘中第一行第二列的b编号为1,它的位置在 1 1 1 1, 1 1 0 1,1 1 0 0,1 1 0 0 右数第14位(从0开始).
如何在一个16位的二进制数中翻转第i枚棋子,使其状态发生变化? 0^1 = 1 1^1=0 b=b^(1<<i) 表示把二进制数b中的第i位取反(0变为1,1变为0) ,所以我们要使第i枚棋子翻转,首先根据 label= 15- 编号,求出这枚棋子是数b中的第几位,然后计算 b^(1<<label),就可以了。
求最少的翻转次数,用BFS广度优先搜索来做。一个step[]数组记录步数.队列中存的是棋盘的每一个状态(即一个16位的二进制数). 把棋盘的初始状态加入队列中,然后一开始16种选择(16个棋子),选择任何一个棋子都可以。当选择一个棋子使其翻转,也要把它周围的棋子翻转,这样就得到了一个新状态tmp , first为队首状态,step[tmp]=step[first]+1. 每次取队首,然后再不断扩充状态。判断当前棋盘状态,如果它等于0 或者 65535,说明棋盘已经呈全黑或全白状态,达到要求。需要注意的是一个到达的新状态可能以前已经到达过,那样如果这样进行下去就进入了死循环,所以用vis[状态] 数组记录当前状态是否到达过,如果到达过就不能再加入队列中。
#include <iostream> #include <string.h> #include <queue> #include <string> using namespace std; int ok=0,vis[65536],step[65536]; void BFS(int d) { if(d==0||d==65535)//棋盘一开始的状态 { cout<<"0"<<endl; ok=1; return; } queue<int> Q; memset(vis,0,sizeof(vis)); vis[d]=1; step[d]=0; Q.push(d); while(!Q.empty()) { int first=Q.front(); Q.pop(); for(int i=0;i<16;i++) { int temp=first; int label=15-i;//求第i枚棋子对应二进制数的第多少位 temp=temp^(1<<label);//处理当前位的棋子 int tm=label+4;//处理当前位上面的棋子 if(tm!=19&&tm!=18&&tm!=17&&tm!=16)//第一排棋子的上面没有棋子 temp=temp^(1<<tm); tm=label-4;//处理当前位下面的棋子 if(tm!=-1&&tm!=-2&&tm!=-3&&tm!=-4)//最后一排棋子的下面没有棋子 temp=temp^(1<<tm); tm=label+1;//处理当前位左面的棋子 if(tm!=4&&tm!=8&&tm!=12&&tm!=16)//最前一列棋子的左面没有棋子 temp=temp^(1<<tm); tm=label-1;//处理当前位右面的棋子 if(tm!=-1&&tm!=3&&tm!=7&&tm!=11)//最后一列棋子的右面没有棋子 temp=temp^(1<<tm); if(temp==0||temp==65535)//达到目标状态 { cout<<step[first]+1<<endl; ok=1; return; } if(vis[temp]==0) { vis[temp]=1; Q.push(temp); step[temp]=step[first]+1; } } } } int main() { char color; int d=0; for(int i=1;i<=4;i++) for(int j=1;j<=4;j++) { cin>>color; d<<=1;//0代表白色 if(color=='b') d=d+1;//1代表黑色 } BFS(d); if(ok==0) cout<<"Impossible"<<endl; return 0; }