题目:
编号为1~8的8个正方形滑块被摆成3行3列(有一个格子空留)。每次可以把与空格相邻的滑块(有公共边才算相邻)移到空格中,而它原来的位置就称为了新的空格。给定初始局面和目标局面(用0表示空格格),你的任务是计算出最少的移动步数。如果无法达到目标局面,则输-1.
输入:
2 6 4 1 3 7 0 5 8
8 1 5 7 3 6 4 0 2
输出:
31
分析与解:
无权图上的最短路问题可以用BFS求解
涉及到hash内部链表的我抽空再补上(我数据结构还没学好- - |)
先说一下大致思路
1.不断移动空格的位置,并将移动的距离进行标记
2.移动过程中会有重复的情况出现,此时利用hash去重
3.一直移动到与目标局面相同,然后输出移动的距离
我写了十分详尽的注释,如下
#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>
using namespace std;
typedef int State[9];//定义状态类型 /*
typedef int a[10];
a b[10];//等同与int b[10][10];
*/const int maxstate = 1000000;
State st[maxstate], goal; //状态数组
/*
等价于int st[maxstate][9]
*/
int dist[maxstate]; //距离数组const int dx[] = {-1, 1, 0 , 0};
const int dy[] = {0, 0, -1, 1};const int hashsize = 1000003;
int head[hashsize], n[maxstate];
/*head[]:哈希表 */ void init_lookup_table()
{memset(head, 0, sizeof(head));
}
int Hash(State & s)
{int v = 0;for (int i = 0; i < 9; i++) v = v * 10 + s[i];//把九个数字合成九位数 return v % hashsize; //hash函数值是不超过hash表的大小的非负整数
}
int try_to_insert(int s)//尾部rear状态
{int h = Hash(st[s]);//把第rear状态下的st中的元素,转换成一个数 (st[rear][i]->h) int u = head[h]; while (u) {if (memcmp(st[u], st[s], sizeof(st[s])) == 0) return 0;//如果之前访问过该结点,那么直接退出 u = n[u]; //顺着链表继续找 }n[s] = head[h];//插入链表中 head[h] = s;return 1;
}int bfs()
{init_lookup_table();//初始化查找表 int fron = 1, rear = 2;//fron,当前状态下标,rear,下一步状态下标 while (fron < rear) {State & s = st[fron]; //此时s引用st[fron],s是一维数组,里面有九个元素 if (memcmp(goal, s, sizeof(s)) == 0) return fron;//如果找到目标状态,返回目标状态在st数组下标 int z;for (z = 0; z < 9; z++) if (!s[z]) break;//找0的位置int x = z/3, y = z%3; //0的行列编号(0-2) for (int i = 0; i < 4; i++) { //上下左右移动零的位置 int newx = x + dx[i]; int newy = y + dy[i];int newz = newx*3 + newy; //newz是z移动之后的下标,t[newz]=0.t[z]=s[newz] if (newx >= 0 && newx < 3 && newy >= 0 && newy < 3) {//如果移动合法 State & t = st[rear]; //开一个新的状态 memcpy(&t, &s, sizeof(s));//把之前的状态(那九个元素)先填到新状态里 t[newz] = s[z]; //之前状态中,下标为z的是零,在新状态下,为零的下标变成newz t[z] = s[newz]; dist[rear] = dist[fron] + 1; //更新节点的距离值 (dist【rear】:走到rear状态时的走的步骤个数) if (try_to_insert(rear)) rear++;//如果成功插入查找表,修改队尾指针(也就是从这个支路向外发散) }}fron++;//扩展完毕修改队首指针,也就是从下一个位置开始找 }return 0;//失败
}int main()
{for (int i = 0; i < 9; i++) scanf("%d", &st[1][i]);//输入初始状态 for (int i = 0; i < 9; i++) scanf("%d", &goal[i]);//输入目标状态 int ans = bfs();// 返回目标状态在st数组下标 if (ans > 0) printf("%d\n", dist[ans]);else printf("-1\n");return 0;
}