C++解题报告:详解经典搜索难题——八数码问题( 双向BFS & A* 求解)

引言

AC这道八数码问题,你和楼教主就是兄弟了。。。


题目描述

在一个3*3的九宫格棋盘里,放有8个数码,数码的数字分别是1~8。棋盘中还有一个位置是空着的,用0表示。可以通过在九宫格里平移数码来改变状态(即空格位在九宫格内能上下左右移动)。数码在任何情况下都不能离开棋盘。给出8个数码的初始状态(没放数码的空格用0表示)和目标状态,问从初始状态到目标状态,最少需要经过多少次移动操作。 例如 初始状态为:

目标状态为:

输入格式

两行 第一行9个数字,用空格隔开,表示初始状态 第二行9个数字,用空格隔开,表示目标状态

输出格式

一个数,即最短路径,如果没有答案,则输出-1

样例

样例输入

2 6 4 1 3 7 0 5 8
8 1 5 7 3 6 4 0 2

样例输出

31

双向BFS求解

(直接讲解有点难,看一下思路,直接看代码吧,代码详解)

看到这道百度之星的难题,内心不知所措,只知道这个要搜索,而且知道DFS一直搜下去必定超时,所以选用BFS,而BFS也会超,就用双向BFS优化,而且我们知道他的起始状态和最终状态。

首先定义一个 map 判定当前状态是否被访问,分为正向搜索(从初始开始)和反向搜索(从最终开始),每次执行一个换位操作,然后判定是否访问,如果没有,则标记为已访问,再看另一向的搜索是否访问过这个状态,如果访问过则找到答案,输出解即可。

在换位操作时,要判定换位后是否在同一行或同一列

​
if( nstep >= 0 && nstep <= 8 && ( nstep/3==step/3 || nstep%3==step%3 ) )
//判定是否越界或换后是否在同一行列

​

如果1~9的全排列,最大的数是987654321,如果我们用一个数组vis[]去记录这个数出现过没有,那该数组最少需要开vis[987654321],空间复杂度相当大!由于全排列中每一个数不重复,所以我们可以把每一个数对应一个映射(即离散化),如123456789最小,它的映射就是1,这样我们发现,离散化后,数字的数量变为了全排列的个数(即9!,362880)。那么我们的vis数组就从亿级变为了十万级,空间复杂度大大

posted @ 2019-05-14 13:20  Nomad_Joe_violet  阅读(79)  评论(0编辑  收藏  举报  来源