该题俩个考点,一个是BFS广度优先遍历,另外一个是通过位的运算。
一。位运算
1.将输入的“0001 1001 1011 1001”字符串转换为十进制数6585,在这里是通过将输入的‘0’或‘1’进行位的左移运算实现。
代码如下:
for(i=0;i<4;i++){
scanf("%s",str);
for(j=0;j<4;j++){
if(str[j]=='b'){ //黑色为1,白色为0,将输入的二进制转换为10进制。eg:0001 1001 1011 1001 转为6585
firstNum += 1<<(4*i+j); //通过左移实现,二进制变成10进制
}else{
firstNum +=0<<(4*i+j);
}
}
}
2.选定一个块,然后实现0变1,1变0的翻转。在这里是通过和1进行异或运算实现0变1,1变0。
代码如下:其中pos的取值是0-15
int flipChess(int number,int pos){// 翻转棋子,和1异或,则反转该值。 即实现0变1,1变0
number = number^(1<<pos); //翻转选中的棋子
int row = pos/4; //行数
int col = pos%4; //列数
if(row > 0){
number = number^(1<<(pos-4)); //翻转上方的棋子,第0行不能翻转
}
if(row<3){
number = number^(1<<(pos+4)); //翻转下方的棋子,第3行(最后一行)不能翻转
}
if(col>0){
number = number^(1<<(pos-1)); //翻转左边的棋子,第0列不能翻转
}
if(col<3){
number = number^(1<<(pos+1)); //翻转右边的棋子,第3列(最后一列)不能翻转
}
return number; //返回翻转后的十进制数
}
二。BFS
1.在这里,我们自定了一个循环队列。相比链队列,循环队列是事先申请好空间即int value[MAX]。该数组存放的就是棋盘对应的十进制数。
广度搜索就是将当前的一个状态值放到循环队列的数组里进行保存。在这里的难点就是将这个棋盘转换为状态值(即十进制数),然后入队。
代码如下:
#define MAX 65535
typedef struct que{ //循环队列,事先初始化数组value,用于存放队列的数据
int value[MAX];
int front;
int rear;
}que;
void initQue(que* q){ //初始化队列
q->front =0;
q->rear = 0;
}
int enQ(que* q , int v){ //入队
if((q->rear+1)%MAX == q->front){ //队列已满
return 0;
}
q->value[q->rear] = v;
q->rear = (q->rear+1)%MAX;
return 1;
}
int deQ(que* q){ //出队
if(q->front == q->rear){ //队列为空 ,返回-1
return -1;
}
int temp = q->value[q->front];
q->front = (q->front+1)%MAX;
return temp;
}
2. 相比DFS , BFS通常用于求解最短或者最少的问题。而DFS则是通常用于求解所有问题。
首先我们将输入的第一个棋盘6585放入队列,接下来依次翻转第0-15个棋子,并得到翻转后的棋盘值。
如果该棋盘是清一色棋盘(即棋盘值为0或者65535),返回所需要的次数。
不是清一色棋盘,加入该棋盘未被搜索过,那么入队,并将次数加一。
在这里,我们定义了一个step[MAX]数组,用于记录棋盘的十进制值是否被检查过以及它的检查次数。
int BFS(int number){ //搜索成功返回当前步骤数,否则返回0
que* q = (que*)malloc(sizeof(que));
initQue(q);
int i=0;
step[number]=0;
enQ(q,number);
while(q->front != q->rear){//队列不为空
int data = deQ(q);
for(i=0;i<16;i++){
int temp=data;
temp=flipChess(temp,i);
if(temp== 0 || temp == 65535){ //搜索成功
return step[data]+1;
}else if(step[temp]==-1){ //当前值没有被搜索过,则入队
enQ(q,temp);
step[temp]=step[data]+1;
}
}
}
return 0 ; //搜索失败返回0
}
最后,我们来看一下全部代码:
#include <stdio.h>
#include <stdlib.h>
char str[5];
#define MAX 65535
int step[MAX];
typedef struct que{ //循环队列,事先初始化数组value,用于存放队列的数据
int value[MAX];
int front;
int rear;
}que;
void initQue(que* q){ //初始化队列
q->front =0;
q->rear = 0;
}
int enQ(que* q , int v){ //入队
if((q->rear+1)%MAX == q->front){ //队列已满
return 0;
}
q->value[q->rear] = v;
q->rear = (q->rear+1)%MAX;
return 1;
}
int deQ(que* q){ //出队
if(q->front == q->rear){ //队列为空 ,返回-1
return -1;
}
int temp = q->value[q->front];
q->front = (q->front+1)%MAX;
return temp;
}
int flipChess(int number,int pos){// 翻转棋盘,和1异或,则反转该值。 即实现0变1,1变0
number = number^(1<<pos); //翻转选中的值
int row = pos/4; //行数
int col = pos%4; //列数
if(row > 0){
number = number^(1<<(pos-4)); //翻转上方的棋盘,第0行不能翻转
}
if(row<3){
number = number^(1<<(pos+4)); //翻转下方的棋盘,第3行(最后一行)不能翻转
}
if(col>0){
number = number^(1<<(pos-1)); //翻转左边的棋盘,第0列不能翻转
}
if(col<3){
number = number^(1<<(pos+1)); //翻转右边的棋盘,第3列(最后一列)不能翻转
}
return number; //返回翻转后的十进制数
}
int BFS(int number){ //搜索成功返回当前步骤数,否则返回0
que* q = (que*)malloc(sizeof(que));
initQue(q);
int i=0;
step[number]=0;
enQ(q,number);
while(q->front != q->rear){//队列不为空
int data = deQ(q);
for(i=0;i<16;i++){
int temp=data;
temp=flipChess(temp,i);
if(temp== 0 || temp == 65535){ //搜索成功
return step[data]+1;
}else if(step[temp]==-1){ //当前值没有被搜索过,则入队
enQ(q,temp);
step[temp]=step[data]+1;
}
}
}
return 0 ; //搜索失败返回0
}
int main()
{
int i =0,j=0;
int firstNum =0;
int result=0;
memset(step,-1,sizeof(step));
//freopen("input.txt","r",stdin);
for(i=0;i<4;i++){
scanf("%s",str);
for(j=0;j<4;j++){
if(str[j]=='b'){ //黑色为1,白色为0,将输入的二进制转换为10进制。eg:0001 1001 1011 1001 转为6585
firstNum += 1<<(4*i+j); //通过左移实现,二进制变成10进制
}else{
firstNum +=0<<(4*i+j);
}
}
}
if(firstNum == 0 || firstNum == 65535){ //如果输入的是清一色棋盘,那么十进制数是0或者65535,所以输出0
printf("0\n");
return 0;
}
result =BFS(firstNum);
if(result){
printf("%d\n",result);
}else{
printf("Impossible\n");
}
return 0;
}
int flipChess(int number,int pos){// 翻转棋盘,和1异或,则反转该值。 即实现0变1,1变0
number = number^(1<<pos); //翻转选中的值
int row = pos/4; //行数
int col = pos%4; //列数
if(row > 0){
number = number^(1<<(pos-4)); //翻转上方的棋盘,第0行不能翻转
}
if(row<3){
number = number^(1<<(pos+4)); //翻转下方的棋盘,第3行(最后一行)不能翻转
}
if(col>0){
number = number^(1<<(pos-1)); //翻转左边的棋盘,第0列不能翻转
}
if(col<3){
number = number^(1<<(pos+1)); //翻转右边的棋盘,第3列(最后一列)不能翻转
}
return number; //返回翻转后的十进制数
}