【搜索算法】八数码问题的多种解法

目录

八数码问题简介:

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

Input 初始:123456780 目标:087654321 初始:123456780 目标:123456708 初始:123456780 目标:123456870 初始:123456780 目标:123456780
Ouput 28 1 No solve 0

[ 1 2 3 4 5 6 7 8 0 ] 1 [ 1 2 3 4 5 6 7 0 8 ] 好比说: \left[\begin{matrix}1 & 2 & 3 \\4 & 5 & 6\\7 & 8 & 0\\\end{matrix}\right]\stackrel{1步}\longrightarrow\left[\begin{matrix}1 & 2 & 3 \\4 & 5 & 6\\7 & 0 & 8\\\end{matrix}\right]
[ 1 2 3 4 5 6 7 8 0 ] 28 [ 0 8 7 6 5 4 3 2 1 ] 好比说:\left[\begin{matrix}1 & 2 & 3 \\4 & 5 & 6\\7 & 8 & 0\\\end{matrix}\right]\stackrel{28步}\longrightarrow\left[\begin{matrix}0 & 8 & 7 \\6 & 5 & 4\\3 & 2 & 1\\\end{matrix}\right]
[ 1 2 3 4 5 6 7 8 0 ] N U L L [ 1 2 3 4 5 6 8 7 0 ] 好比说:\left[\begin{matrix}1 & 2 & 3 \\4 & 5 & 6\\7 & 8 & 0\\\end{matrix}\right]\stackrel{NULL}\longrightarrow\left[\begin{matrix}1 & 2& 3 \\4 & 5 & 6\\8 & 7 & 0\\\end{matrix}\right] ios

判断是否有解:

这是一个很是经典的搜索问题,可是有时候咱们会发现从 初始状态 到达不了 目标状态 ,这时候咱们就须要提早判断是否有解 ( 否则无解的时候搜索算法会一直搜 )web

先说结论:算法

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

例如: 123456780213456780
逆序对和数为 2827
因此没法互相到达app

证实:

证实其必要性: 若是两个状态逆序对的和奇偶性不一样,则必然不能互相抵达svg

首先,对于 3 × 3 3\times3 的格子,当 0 0 元素与任意元素交换(进行移动),表如今压缩以后的长串数字上分别是:
左移:将 0 0 与前一位交换,此时总逆序奇偶性不变函数

由于 0 0 自己不算在逆序对计算内,整体顺序没有改变布局

右移:将 0 0 与后一位交换,同左移
上移:将 0 0 与前第三位交换,此时总逆序奇偶性不变优化

假设被 0 0 交换元素是 A,中间元素有两个,分别是B,C

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

下移:将 0 0 与后第三位交换,同上移

证实其充分性: 若是两个状态逆序对的和奇偶性相同,则一定能够互相抵达

: [ A B C 0 D E F G H ] 11 [ A B 0 C D E F G H ] 看状态: \left[\begin{matrix}A&amp; B &amp; C\\0 &amp; D &amp; E\\F &amp; G &amp; H\\\end{matrix}\right]\stackrel{11步}\longrightarrow\left[\begin{matrix}A&amp; B &amp; 0\\C &amp; D &amp; E\\F &amp; G &amp; H\\\end{matrix}\right]

能够看到这两个状态是能够相互抵达的
对应: A B C 0 D E F G H A B 0 C D E F G H A B C 0 D E F G H \longrightarrow A B 0 C D E F G H
其余左右移同理
也就是说任意 左移和右移 的步骤均可以相互抵达,又由于左移和右移不改变逆序对奇偶性
则有

能够将全部偶数逆序对和的状态转化成: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是什么,该怎么用
能够看这位前辈博客:https://blog.csdn.net/qq_39836658/article/details/78560819
(另外实际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限定呦~
在这里插入图片描述

使用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;
}

大体速度:
在这里插入图片描述

康托(cantor)展开:

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

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

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

V = i = 1 n A i × ( n i ) ! 有公式: V = \sum_{i=1}^nA_i \times (n-i)! V V 表明最后获得的 H a s h Hash
n n 表明目标的元素个数
A i A_i 表明第 i i 个元素后面比此元素小的元素个数

代码:
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,然而仍是有限深
在这里插入图片描述
BFS发挥稳定,快了一点:
在这里插入图片描述

双向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能这么快真是超出个人想象了,估计是由于数据特殊的缘由
在这里插入图片描述
在这里插入图片描述

A*算法:

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

具体公式表现为: f ( n ) = g ( n ) + h ( n ) f^*(n)=g^*(n)+h^*(n) f ( n ) f^*(n) 表明对节点 n 评估结果
g ( n ) g^*(n) 表明原始节点到当前节点 n 的实际步数
h ( n ) h^*(n) 表明当前节点 n 到目标节点的估计步数,咱们称之为 启式发函数

值得注意的是,咱们把 h ( n ) h(n) 表明为当前节点到目标节点的实际步数
那么能够证实若是有 h ( n ) h ( n ) h^*(n)\leq h(n) 则一定能够找到最优解,一样的,若是 h ( n ) h^*(n) 越接近 h ( n ) h(n) ,则搜索效率越高

  • h ( n ) = h ( n ) h^*(n)= h(n) 时,拥有最高的效率
  • h ( n ) = 0 h^*(n)= 0 时,将退化成 Dijkstra算法
  • h ( n ) &gt; h ( n ) h^*(n)&gt; h(n) 时,能够很快的搜索到目标,可是未必是最优解

那么对于八数码问题,咱们能够设定多种启发式函数:

  • h ( n ) = h^*(n) = 当前状态和目标状态格子上不一样的数字个数
  • h ( n ) = 0 0 h^*(n) = 当前状态的 0 跟目标状态的 0 相距的格数
  • 还有不少,经常使用的是上面两种
使用第一种启发式函数的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;
}

有一些优化,可是不是很是明显,固然若是有更好的启发式函数会更快
在这里插入图片描述
在这里插入图片描述

迭代加深搜索:

迭代加深搜索实际上就是逐渐加大限制深度的DFS搜索
好比说对于八数码问题,因为不知道最大深度,咱们只能提早预约一个最大的迭代深度,但这样对于解规模较小的答案,至关于浪费了大量时间搜索到最大迭代深度
因此说咱们能够在搜索中 逐渐加大迭代深度

好比说:

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

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

举个简单的例子说明,假如说每一个节点能够扩展两个节点
对于 BFS 来说,第 n n 层的拓展节点数是 2 n 2^n
而对于 迭代加深搜索 来说,第一次扩展的节点数是 1 = 2 1 1 1=2^1-1 ,第二次扩展的节点数是 1 + 2 = 2 2 1 1+2=2^2-1 ,第 n 1 n-1 次拓展的节点数是 2 n 1 1 2^{n-1}-1
其前 n n 次拓展的节点数和为 2 n 2 n 2^n-2n ,也就是说重复的遍历之前的节点代价相对于拓展下一层来讲并不高,对于每一个节点能够拓展更多节点的状况更是如此

迭代加深搜索代码:

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

#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;
}

还算比较快的,重点是空间占用少
在这里插入图片描述