八数码问题解析(BFS搜索)
在一个3*3的棋盘上放置编号为1~8的8个方块,每个占一格,另外还有一格空格。与空格相邻的数字方块可以移动到空格里。指定初始
输入样例:
1 2 3 0 8 4 7 6 5
1 0 3 8 2 4 7 6 5
输出样例:
2
初始状态:
1 | 2 | 3 |
---|---|---|
8 | 4 | |
7 | 6 | 5 |
目标状态:
1 | 3 | |
---|---|---|
8 | 2 | 4 |
7 | 6 | 5 |
重点:
1.利用康拓展开判重
2.合理利用队列
3.利用一维坐标和二维坐标
#include <iostream>
#include <queue>
const int LEN = 362880;//康拓展开的状态共有9!=362880种
using namespace std;
struct node {//用一维的数组记录一个状态
int state[9];//记录状态
int dis;//记录从初始到此状态需要多少步数
};
int dir[4][2] = { {-1,0},{0,-1},{1,0},{0,1} };//作为移动的四个方向
int start[9];//初始状态的位置
int goal[9];//目标状态的位置
//康拓展开需要用到的数据
long int factory[] = { 1,1,2,6,24,120,720,5040,40320,362880 };
long visited[LEN] = { 0 };
//利用康托展开判重
bool Cantor(int str[], int n) {
long result = 0;//记录当前位置的编号
for (int i=0;i<n;i++){
int counted = 0;
for (int j = i + 1; j < n; j++) {
if (str[i] > str[j])//判断当前未出现的元素排在第几个
counted++;
}
result += counted * factory[n - i - 1];
}
if (!visited[result]) {//如果当前状态并没有出现过
//则将此状态标记,并返回1
visited[result] = 1;
return 1;
}
else {//如果出现过,则说明这个状态在之前就已经被走到了
return 0;
}
}
int dfs() {
node head;
memcpy(head.state, start, sizeof(head.state));//将初始状态的位置输入
head.dis = 0;
queue<node> q;//创建存储状态的队列
q.push(head);//将初始状态加入
while (!q.empty()){//当队列中没有任何状态时停止循环
node head = q.front();//将队列中第一个状态取出
if (head.state == goal) {
return head.dis;//如果和目标状态相同则结束循环,将步数返回
}
//没有的话则继续循环,利用head将下一个状态推导出,并加入队列
q.pop();
int z = 0;//用于储存head中的0的一维位置
for (int i = 0; i < 9; i++) {
if (head.state[i] == 0) {
z = i;
break;
}
}
//利用z将坐标转化为二维
int x = z % 3;
int y = z / 3;//此x,y坐标为原状态的0的二维坐标,利用这个坐标和dir数组中的四个方向,得出head状态的四种移动方式的结果
for (int i = 0; i < 4; i++) {//将head移动后的4种状态依次列出
int newx = x + dir[i][0];
int newy = y + dir[i][1];
int newz = newx + 3 * newy;//将新的坐标转化为一维,因为存储状态需要一维坐标
//判断移动后是否越界
if (newx >= 0 && newx < 3 && newy >= 0 && newy < 3) {
node newnode;//没有越界,创建下一个状态
memcpy(&newnode, &head, sizeof(node));//获得下一个状态需要得到上一个状态中的信息,然后通过新的坐标来更新上一个状态
//上一个状态还要继续参加下一个循环,所以在此需要创建一个新的状态来获得老状态的信息,代替老状态完成更新,并添加到队列中
//更新状态
swap(newnode.state[z], newnode.state[newz]);//0的位置变换,直接交换即可
newnode.dis++;//步数加一
if (Cantor(newnode.state, 9)) {//利用康拓展开判重
//若此状态没有出现过,则此状态是第一次出现
q.push(newnode);//将此状态添加到队列中
}
}
}
return -1;//while循环结束,若执行到这一步,则说明将所有状态走完都没有得到目标状态,没有找到,返回-1
}
}
int main() {
//将初始位置和目标位置输入
for (int i = 0; i < 9; i++)cin>> start[i];
for (int i = 0; i < 9; i++)cin >> goal[i];
int num = dfs();
if (num == -1)cout << "Impossible" << endl;
else cout << num << endl;
return 0;
}