目录

八数码问题简介
判断是否有解
朴素的 DFS 和 BFS
对于 DFS 和 BFS 剪枝 (去重)
  • 数据结构 map
  • 康托展开
双向BFS
A*算法
IDA算法 - 迭代加深的DFS
输出路径的方法

八数码问题简介:

3×3 的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。
棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。
要求解的问题是: 给出一种初始布局(初始状态)和目标布局,找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变
(为了简化棋盘,我们把每个数字按每行缩成一个长串)

Input

初始:123456780 目标:087654321

初始:123456780 目标:123456708

初始:123456780 目标:123456870

初始:123456780 目标:123456780

Ouput

28

1

No solve

0

greedy算法解决8数码问题 求解八数码问题_greedy算法解决8数码问题
greedy算法解决8数码问题 求解八数码问题_逆序_02
greedy算法解决8数码问题 求解八数码问题_八数码问题_03

判断是否有解:

这是一个非常经典的搜索问题,但是有时候我们会发现从 初始状态 到达不了 目标状态 ,这时候我们就需要提前判断是否有解 ( 不然无解的时候搜索算法会一直搜 )

先说结论:

把棋盘状态表示成一维的形式,求出除 0 之外所有数字的逆序数之和,称为这个状态的逆序
(也就是每个数字前面比它大的数字的个数的)
若两个状态的逆序 奇偶性相同 ,则 可相互到达,否则 不可相互到达

例如: 123456780213456780
逆序对和数为 2827
所以无法互相到达

证明:

证明其必要性: 如果两个状态逆序对的和奇偶性不同,则必然不能互相抵达

首先,对于 greedy算法解决8数码问题 求解八数码问题_初始状态_04 的格子,当 greedy算法解决8数码问题 求解八数码问题_greedy算法解决8数码问题_05 元素与任意元素交换(进行移动),表现在压缩之后的长串数字上分别是:
左移:将 greedy算法解决8数码问题 求解八数码问题_greedy算法解决8数码问题_05

因为 greedy算法解决8数码问题 求解八数码问题_初始状态_07

右移:将 greedy算法解决8数码问题 求解八数码问题_greedy算法解决8数码问题_05 与后一位交换,同左移
上移:将 greedy算法解决8数码问题 求解八数码问题_greedy算法解决8数码问题_05

假设被 greedy算法解决8数码问题 求解八数码问题_初始状态_07

  1. 如果有 A>B 和 A>C 则总体逆序对个数 -2 ,奇偶性不变
  2. 如果有 A>B 和 A<C 则总体逆序对不变 ,奇偶性不变
  3. 如果有 A<B 和 A>C 同理 2
  4. 如果有 A<B 和 A<C 则总体逆序对个数 +2, 奇偶性不变

下移:将 greedy算法解决8数码问题 求解八数码问题_greedy算法解决8数码问题_05 与后第三位交换,同上移

证明其充分性: 如果两个状态逆序对的和奇偶性相同,则必定可以互相抵达

greedy算法解决8数码问题 求解八数码问题_搜索算法_12

可以看到这两个状态是可以相互抵达的
对应: greedy算法解决8数码问题 求解八数码问题_逆序_13
其他左右移同理
也就是说任意 左移和右移 的步骤都可以相互抵达,又因为左移和右移不改变逆序对奇偶性
则有

可以将所有偶数逆序对和的状态转化成:0 1 2 3 4 5 6 7 8
可以将所有奇数逆序对和的状态转化成:0 2 1 3 4 5 6 7 8
因为根据前面的左右移无限制证明,你可以通过有限次操作将最大的元素放在最后面,同时把次大的元素放在倒数第二位而不打乱最后一位,前推同理,直到达到状态 0 1 2 和 0 2 1 此时因为位数限制,无法继续这样操作,所以证得

同样的由于所有偶状态和所有奇状态都收束与不同的一个状态,根据移动的可逆性,故如果两个状态逆序对的和奇偶性相同,则必定可以互相抵达

代码:
int P(string S){
	int jShu = 0;
	for(int i=0; i<9; i++) for(int j=0; j<i; j++) if(S[i]>S[j]&&S[j]!='0') jShu++;
 	return jShu; //返回逆序对和,把两个状态的逆序对和都%2,相等则有解,不等则无解
}

朴素的 DFS 和 BFS:

判断了是否有解,接下来就是看如何搜索,总所周知,最基础的搜索算法有两种: 深搜(DFS)广搜(BFS)
对于朴素的DFS和BFS而言,显然这道题用广搜更好,因为是找最小步骤,如果是深搜,如果不知道最小步数限制,则会一直在一个分支中搜索,而且第一次搜到的解未必是最小解,而广搜则会更快地找到最小解 ( 因为是平铺的往下搜,所以第一次碰到的解一定是最小解 ),所以说在这里只贴朴素BFS的代码

朴素BFS:

可以用,但由于没有判重,大步数会超时(甚至死循环
例如:
123456780
087654321
28
inti.h头文件代码传送

#include"init.h"

queue<string> NS;  //记录字符的队列
queue<int> Zhixy;  //记录步数,0的位置压缩后的队列
int MinZhi;
 
int main(){
 	if(Init()){
		cout<<"No Solve"<<endl;
  		return 0;
	}
 	NS.push(S);
 	for(int i=0; i<9; i++) if(S[i]=='0') Zhixy.push((int)(i/3)*10+i%3);
 	while(!NS.empty()){
  		string S2 = NS.front(); NS.pop();
  		int A = Zhixy.front(); Zhixy.pop();
 		int Zhi = A/100, x = (A/10)%10, y = A%10;
 		//用了数据压缩,Zhi代表目前状态到原状态步数,x和y代表现在0的位置,把它们压成一个数存进队列节约空间
  		if(S2==S_Goal){  //找到即可退出,必定是最短
   			MinZhi = Zhi;
  			break;
  		}
  		if(x+1<3){
      		NS.push(exChange(S2, x+1, y, x, y));
      		Zhixy.push((x+1)*10+y+(Zhi+1)*100);
  		}
    		if(y+1<3){
      		NS.push(exChange(S2, x, y+1, x, y));
      		Zhixy.push(x*10+y+1+(Zhi+1)*100);
    		}
    		if(x-1>=0){
      		NS.push(exChange(S2, x-1, y, x, y));
      		Zhixy.push((x-1)*10+y+(Zhi+1)*100);
    		}
    		if(y-1>=0){
     		NS.push(exChange(S2, x, y-1, x, y));
      		Zhixy.push(x*10+y-1+(Zhi+1)*100);
    		}
 	}
 	cout<<MinZhi<<endl;
 	TimeB("传统BFS算法");
	string B = S_Goal;
 	return 0;
}

<inti.h> 头文件的代码:

因为每个程序有一部分代码是完全一样的,所以就改成头文件了XD

#include<sys/time.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<queue>
#include<cmath>
#include<ctime>
#include<map>
using namespace std;

string S, S_Goal;
struct timeval start, end; 

//测时函数 
void TimeA(){
    	gettimeofday(&start,NULL);
}
void TimeB(string S){
 	gettimeofday(&end,NULL);
 	cout<<S<<": "<<(end.tv_sec-start.tv_sec)+(double)(end.tv_usec-start.tv_usec)/(double)1000000<<"s"<<endl;
}
//字符串元素位置替换
string exChange(string S3, int x1, int y1, int x2, int y2){
 	char Bet = S3[x1*3+y1];
 	S3[x1*3+y1] = S3[x2*3+y2];
 	S3[x2*3+y2] = Bet;
 	return S3;
}
//判断是否有解
int P(string S){
 	int jShu = 0;
 	for(int i=0; i<9; i++) for(int j=0; j<i; j++) if(S[i]>S[j]&&S[j]!='0') jShu++;
 	return jShu;
}
//初始化,有解返回0,无解返回1
bool Init(){
 	cout<<"目标九宫格:";
 	cin>>S_Goal;
 	cout<<"已有九宫格:";
 	cin>>S;
 	TimeA();
 	if(P(S_Goal)%2!=P(S)%2) return 1;
 	else return 0;
}

对于 DFS 和 BFS 剪枝 (去重)

对于这两个搜索来说,很显然有一个优化方法:
BFS:如果当前状态我以前搜过,那么我就不需要继续搜这个状态
DFS:如果当前状态我以前搜过,且当前状态步数比我以前搜到这个状态用的步数多,则不继续往下搜,反之则还是需要往下搜
这样去重以后,不仅可以大幅节省搜索时间,也可以避免死循环的产生

那么怎么判断当前状态是否搜过呢?

Map数据结构:

C++为我们提供了一个非常方便的数据结构Map,至于Map是什么,该怎么用
可以看这位前辈博客:
(另外实际Map由于各种原因,速度是稍微慢一点的,但对于解八数码问题已经绰绰有余了)

使用Map的DFS:

需要限制最大搜索深度,这里限制深度30层,挺慢的,一般都会超时
剪枝思路: 如果当前状态我以前搜过,且当前状态步数比我以前搜到这个状态用的步数多,则不继续往下搜,反之则还是需要往下搜
inti.h头文件代码传送

#include"init.h"

map<string, int> Map;
int MinZhi = 30;

void DFS(int jShu, string S2, int x, int y){
 	if(S2==S_Goal){
  		if(MinZhi>jShu) MinZhi = jShu;
  		return;
 	}
        //去重部分
 	if(Map.count(S2)){
  		if(Map[S2]>jShu) Map[S2] = jShu;
 		else return;
 	}
 	else Map[S2] = jShu;
 	if(jShu>=MinZhi) return;
 	string Bet = S2;
 	if(x+1<3){
  		S2 = exChange(S2, x+1, y, x, y);
  		DFS(jShu+1, S2, x+1, y);
  		S2 = Bet;
 	}
 	if(y+1<3){
  		S2=exChange(S2, x, y+1, x, y);
  		DFS(jShu+1, S2, x, y+1);
  		S2 = Bet;
 	}
 	if(x-1>=0){
  		S2=exChange(S2, x-1, y, x, y);
  		DFS(jShu+1, S2, x-1, y);
  		S2 = Bet;
 	}
 	if(y-1>=0){
  		S2=exChange(S2, x, y-1, x, y);
  		DFS(jShu+1, S2, x, y-1);
  		S2 = Bet;
 	}
}
int main(){
 	if(Init()){
  		cout<<"No Solve"<<endl;
  		return 0;
 	}
 	int x, y;
 	for(int i=0; i<9; i++) if(S[i]=='0') x=i/3, y=i%3;
 	DFS(0, S, x, y);
 	cout<<MinZhi<<endl;
 	TimeB("传统DFS+Map");
}

大致速度:(不仅有步数限深,还有TLE限定呦~

greedy算法解决8数码问题 求解八数码问题_初始状态_14

使用Map的BFS:

inti.h头文件代码传送剪枝思路: 如果当前状态我以前搜过,那么我就不需要继续搜这个状态,同样的,第一次搜到的相同状态一定是最短解

#include"init.h"

map<string, int> Map;
map<string, string> Map2;
queue<string> NS;
queue<int> Zhixy;
int MinZhi;

int main(){
 	if(Init()){
  		cout<<"No Solve"<<endl;
  		return 0;
	}
 	NS.push(S);
 	for(int i=0; i<9; i++) if(S[i]=='0') Zhixy.push((int)(i/3)*10+i%3);
 	while(!NS.empty()){
  		string S2 = NS.front(); NS.pop();
  		int A = Zhixy.front(); Zhixy.pop();
  		int Zhi = A/100, x = (A/10)%10, y = A%10;
  		if(S2==S_Goal){
   			MinZhi = Zhi;
   			break;
  		}
  		//去重部分
  		if(Map.count(S2)) continue;
  		else Map[S2] = Zhi;
  	    	if(x+1<3){
   			NS.push(exChange(S2, x+1, y, x, y));
   			Zhixy.push((x+1)*10+y+(Zhi+1)*100);
		}
  		if(y+1<3){
   			NS.push(exChange(S2, x, y+1, x, y));
   			Zhixy.push(x*10+y+1+(Zhi+1)*100);
  		}
  		if(x-1>=0){
   			NS.push(exChange(S2, x-1, y, x, y));
   			Zhixy.push((x-1)*10+y+(Zhi+1)*100);
  		}
  		if(y-1>=0){
  			NS.push(exChange(S2, x, y-1, x, y));
   			Zhixy.push(x*10+y-1+(Zhi+1)*100);
  		}
	}
 	cout<<MinZhi<<endl;
 	TimeB("传统BFS算法+Map");
 	return 0;
}

大致速度:

greedy算法解决8数码问题 求解八数码问题_逆序_15



康托(cantor)展开:

竟然Map速度不算很快,那我们有什么其它方法来解决去重问题,其实在使用Map时,我们已经用到了Hash,那么对于八数码问题,很显然我们看出所有的棋盘状态其实就是 0~8 的全排列,而对于全排列的Hash,我们可以采用 康托展开

康托展开 是一个全排列到一个自然数的双射,常用于构建哈希表时的空间压缩
康托展开 的实质是计算当前排列在所有由小到大全排列中的位次,而且是可逆的
例如 012345678 就是 0;102345678 就是 1

那么如何对一组数进行康托展开

greedy算法解决8数码问题 求解八数码问题_八数码问题_16greedy算法解决8数码问题 求解八数码问题_greedy算法解决8数码问题_17 代表最后得到的greedy算法解决8数码问题 求解八数码问题_逆序_18
greedy算法解决8数码问题 求解八数码问题_八数码问题_19 代表目标的元素个数
greedy算法解决8数码问题 求解八数码问题_greedy算法解决8数码问题_20 代表第 greedy算法解决8数码问题 求解八数码问题_greedy算法解决8数码问题_21

代码:
int Factorial[9]={1, 1, 2, 6, 24, 120, 720, 5040, 40320}; //康托函数需要的阶乘
int Cantor(string S){ //康拓函数
 	int jShu = 0;
 	for(int i=0; i<9; i++){
  		int jShu2 = 0;
  		for(int j=i+1; j<9; j++) if(S[i]>S[j]) jShu2++;
  		jShu += jShu2*Factorial[8-i];
 	}
 	return jShu;
}

具体实现代码就不贴了XD

成效:

DFS用Cantor展开快了不少,甚至有希望避免TLE的悲惨命运Orz,然而还是有限深

greedy算法解决8数码问题 求解八数码问题_greedy算法解决8数码问题_22


BFS发挥稳定,快了一点:

greedy算法解决8数码问题 求解八数码问题_八数码问题_23



双向BFS搜索:

介绍待更…
inti.h头文件代码传送 下面是用 Map 去重的代码:

#include"init.h"

map<string, int> Map;
queue<string> NS;
queue<int> Zhixy;
int MinZhi;

int main(){
 	if(Init()){
  		cout<<"No Solve"<<endl;
  		return 0;
 	}
 	NS.push(S+"2"); NS.push(S_Goal+"1");
 	for(int i=0; i<9; i++) if(S[i]=='0') Zhixy.push(100+(int)(i/3)*10+i%3);
 	for(int i=0; i<9; i++) if(S_Goal[i]=='0') Zhixy.push(100+(int)(i/3)*10+i%3);
 	while(!NS.empty()){
  		string S1 = NS.front(); 
  		NS.pop();
  		bool sign = S1[9]-'1';
  		string S2(S1.substr(0,9)); //有点乱,有时间我整理一下吧
  		int A = Zhixy.front(); 
  		Zhixy.pop();
  		int Zhi = A/100, x = (A/10)%10, y = A%10;
  		if(Map.count(S2)){
   			int bet = Map[S2];
   			if((sign&&bet<0)||(!sign&&bet>0)){
    				cout<<Zhi+abs(Map[S2])-2<<endl;
    				break;
   			}
   			else if(abs(bet)>Zhi){
    				if(sign) Map[S2] = Zhi;
    				else Map[S2] = -Zhi;
   			}
   			else continue;
  		}
  		else{
   			if(sign){
    				Map[S2] = Zhi;
    				S2 = S2+"2";
   			}
    			else{
    				Map[S2] = -Zhi;
    				S2 = S2+"1";
    			}
  		}
  		if(x+1<3){
   			NS.push(exChange(S2, x+1, y, x, y));
   			Zhixy.push((x+1)*10+y+(Zhi+1)*100);
		}
  		if(y+1<3){
   			NS.push(exChange(S2, x, y+1, x, y));
   			Zhixy.push(x*10+y+1+(Zhi+1)*100);
  		} 
  		if(x-1>=0){
   			NS.push(exChange(S2, x-1, y, x, y));
   			Zhixy.push((x-1)*10+y+(Zhi+1)*100);
  		}
  		if(y-1>=0){
   			NS.push(exChange(S2, x, y-1, x, y));
   			Zhixy.push(x*10+y-1+(Zhi+1)*100);
  		}
	 }
	TimeB("双向BFS算法"); 
 	return 0;
}

讲道理,双向BFS能这么快真是超出我的想象了,估计是因为数据特殊的原因

greedy算法解决8数码问题 求解八数码问题_搜索算法_24


greedy算法解决8数码问题 求解八数码问题_初始状态_25



A*算法:

A*算法 可以通过当前节点状态和以后预估的状态来有选择的拓展节点,从而更快的抵达搜索目标

具体公式表现为:greedy算法解决8数码问题 求解八数码问题_初始状态_26 greedy算法解决8数码问题 求解八数码问题_初始状态_27 代表对节点 n 评估结果
greedy算法解决8数码问题 求解八数码问题_八数码问题_28 代表原始节点到当前节点 n 的实际步数
greedy算法解决8数码问题 求解八数码问题_greedy算法解决8数码问题_29 代表当前节点 n 到目标节点的估计步数,我们称之为 启式发函数

值得注意的是,我们把 greedy算法解决8数码问题 求解八数码问题_初始状态_30 代表为当前节点到目标节点的实际步数
那么可以证明如果有 greedy算法解决8数码问题 求解八数码问题_逆序_31 则必定可以找到最优解,同样的,如果 greedy算法解决8数码问题 求解八数码问题_greedy算法解决8数码问题_32 越接近 greedy算法解决8数码问题 求解八数码问题_初始状态_30,则搜索效率越高

  • greedy算法解决8数码问题 求解八数码问题_初始状态_34
  • greedy算法解决8数码问题 求解八数码问题_初始状态_35 时,将退化成 Dijkstra算法
  • greedy算法解决8数码问题 求解八数码问题_逆序_36

那么对于八数码问题,我们可以设定多种启发式函数:

  • greedy算法解决8数码问题 求解八数码问题_逆序_37
  • greedy算法解决8数码问题 求解八数码问题_逆序_38
  • greedy算法解决8数码问题 求解八数码问题_八数码问题_39
使用第一种启发式函数的A*算法代码:

代码介绍待更
inti.h头文件代码传送

#include"init.h"

struct Node{
    	int Fn, Num;
    	string S1;
    	bool operator < (const Node & a) const{ return Fn>a.Fn; }
};

int Compare(string a, string b){
 	int jShu = 0;
 	for(int i=0; i<9; i++) if(a[i]!=b[i]) jShu++;
 	return jShu;
}

priority_queue<Node> NS;
map<string, int> Map;

int main(){
 	if(Init()){
  		cout<<"No Solve"<<endl;
  		return 0;
 	}
 	Node Head;
 	Head.Fn = 0; Head.S1 = S;
 	for(int i=0; i<9; i++) if(S[i]=='0') Head.Num = (int)(i/3)*10+i%3;
 	NS.push(Head);
	while(!NS.empty()){
  		Node b, a = NS.top(); NS.pop();
  		int Zhi = a.Num/100, x = (a.Num/10)%10, y = a.Num%10;
  
  		if(a.S1 == S_Goal){
   			cout<<Zhi<<endl;
   			break;
  		}
		if(Map.count(a.S1)){
   			if(Map[a.S1]>Zhi) Map[a.S1] = Zhi;
   			else continue;
  		}
  		else Map[a.S1] = Zhi;
		if(x+1<3){
  			b.S1 = exChange(a.S1, x+1, y, x, y);
   			b.Fn = Zhi + Compare(b.S1, S_Goal);
   			b.Num = (x+1)*10+y+(Zhi+1)*100;
   			NS.push(b);
		}
  		if(y+1<3){
   			b.S1 = exChange(a.S1, x, y+1, x, y);
   			b.Fn = Zhi + Compare(b.S1, S_Goal);
   			b.Num = x*10+y+1+(Zhi+1)*100;
   			NS.push(b);
  		} 
  		if(x-1>=0){
   			b.S1 = exChange(a.S1, x-1, y, x, y);
   			b.Fn = Zhi + Compare(b.S1, S_Goal);
   			b.Num = (x-1)*10+y+(Zhi+1)*100;
   			NS.push(b);
		}
  		if(y-1>=0){
   			b.S1 = exChange(a.S1, x, y-1, x, y);
   			b.Fn = Zhi + Compare(b.S1, S_Goal);
   			b.Num = x*10+y-1+(Zhi+1)*100;
   			NS.push(b);
  		}
  	}
 	TimeB("A_Star算法+Map"); 
 	return 0;
}

有一些优化,但是不是非常明显,当然如果有更好的启发式函数会更快

greedy算法解决8数码问题 求解八数码问题_greedy算法解决8数码问题_40


greedy算法解决8数码问题 求解八数码问题_逆序_41



迭代加深搜索:

迭代加深搜索实际上就是逐渐加大限制深度的DFS搜索
比如说对于八数码问题,由于不知道最大深度,我们只能提前预定一个最大的迭代深度,但这样对于解规模较小的答案,相当于浪费了大量时间搜索到最大迭代深度
所以说我们可以在搜索中 逐渐加大迭代深度

比如说:

  • 开始迭代深度为 1,搜索一次,没有解就退出
  • 之后迭代深度为 2,搜索一次,没有解就退出
  • 最后迭代深度为 n,搜索一次,有解,则最短步数为 n

那么为什么不用 BFS 而用 迭代加深搜索 呢?
首先,迭代加深搜索不像DFS一样,需要大量空间来存储要遍历的节点
其次,迭代加深搜索看似时间复杂度很高 (因为不断的重复搜索),但实际上它的时间复杂度跟 BFS 是相同的

举个简单的例子说明,假如说每个节点可以扩展两个节点
对于 BFS 来讲,第 greedy算法解决8数码问题 求解八数码问题_八数码问题_42 层的拓展节点数是 greedy算法解决8数码问题 求解八数码问题_greedy算法解决8数码问题_43
而对于 迭代加深搜索 来讲,第一次扩展的节点数是 greedy算法解决8数码问题 求解八数码问题_八数码问题_44,第二次扩展的节点数是 greedy算法解决8数码问题 求解八数码问题_逆序_45,第 greedy算法解决8数码问题 求解八数码问题_搜索算法_46 次拓展的节点数是 greedy算法解决8数码问题 求解八数码问题_greedy算法解决8数码问题_47
其前 greedy算法解决8数码问题 求解八数码问题_八数码问题_42 次拓展的节点数和为 greedy算法解决8数码问题 求解八数码问题_初始状态_49,也就是说重复的遍历以前的节点代价相对于拓展下一层来说并不高,对于每个节点可以拓展更多节点的情况更是如此

迭代加深搜索代码:

加入了剪枝,具体细节会在以后更新

#include"init.h"

int jShu;
bool B;

int Compare(string a, string b){
 	int jShu2 = 0;
 	for(int i=0; i<9; i++) if(a[i]!=b[i]) jShu2++;
	return jShu2;
}

void IDA(int jShu2, string S2, int x, int y, int u){
 	if(S2==S_Goal){
  		B=1;
  		return;
 	}
 	if(B) return;
 	if(Compare(S2, S_Goal) + jShu2 - 1>jShu) return;
 	string S3;
 	if(x+1<3&&u!=3){
  		S3 = S2;
  		IDA(jShu2+1, exChange(S3, x+1, y, x, y), x+1, y, 0);
 	}
 	if(y+1<3&&u!=2){
  		S3 = S2;
  		IDA(jShu2+1, exChange(S3, x, y+1, x, y), x, y+1, 1);
 	} 
 	if(x-1>=0&&u!=0){
  		S3 = S2;
	  	IDA(jShu2+1, exChange(S3, x-1, y, x, y), x-1, y, 3);
	}
 	if(y-1>=0&&u!=1){
  		S3 = S2;
  		IDA(jShu2+1, exChange(S2, x, y-1, x, y), x, y-1, 2);
 	}
}

int main(){
 	if(Init()){
  		cout<<"No Solve"<<endl;
  		return 0;
 	}
 	int x, y;
 	for(int i=0; i<9; i++) if(S[i]=='0') x=i/3, y=i%3;
 	while(1){
  		IDA(0, S, x, y, -1);
  		if(B){
   			cout<<jShu<<endl;
   			break;
  		}
  		jShu++;
 	}
 	TimeB("IDA算法");
 	return 0;
}

还算比较快的,重点是空间占用少

greedy算法解决8数码问题 求解八数码问题_greedy算法解决8数码问题_50