广度优先搜索寻找最优路径、以及双向广度搜索算法 .

这里是poj1915上的一道在棋盘上搜索走步路径的题目:

代码如下(使用BFS):

/*
 *	POJ 1915
 *	使用广度优先搜索寻找最佳路径
 *	本题可以优化的地方:如果只是为了获得最小的到达目标节点的步数,
 *	那么可以直接在Node的结构体中定义step域,然后在搜索过程中逐步更新step 
*/

#include<iostream>
#include<queue>
#include<stack>
#include<ctime>
using namespace std;
#define MAXSIZE 301
#define NOT !

int size;
int startX,startY,endX,endY;
int minStep;
typedef struct Node{								//node结构体
	int x;				
	int y;
	int myindex;
	int parent;
}*PNode;

int direction[8][2]={{-2,-1},{-1,-2},{-2,1},{-1,2},{1,-2},{2,-1},{1,2},{2,1}};		//骑士可能的前进方向
bool visited[MAXSIZE][MAXSIZE];					//记录当前节点是否被访问过,默认初始化为false
queue<Node> Queue;
Node start,End;									//开始节点和结束节点
PNode NodeTable[MAXSIZE*MAXSIZE]={NULL};		//存储节点的表
static int id;									//用于记录一次访问到的节点的ID值

void init(){

	id=0;
	visited[startX][startY]=true;
	start.x=startX;
	start.y=startY;
	start.myindex=0;
	start.parent=-1;
	Queue.push(start);
	End.x=endX;
	End.y=endY;

	NodeTable[id]=new Node();							//注意一定要先new分配内存空间,再进行赋值
	*NodeTable[id]=start;
	//cout<<"Queue.size()=" <<Queue.size()<<endl;
	minStep=0;
}


void BFS(){

	int k;
	bool Finish=false;
	while(NOT Queue.empty() && NOT Finish ){

		Node curr=Queue.front();
		Queue.pop();
		
		if( endX==curr.x && endY==curr.y){
			Finish=true;
			End.parent=curr.parent;
			continue;
		}
		for(k=0;k<8;k++){

			Node tmp=curr;
			tmp.x=curr.x+direction[k][0];
			tmp.y=curr.y+direction[k][1];

			if( tmp.x<0 || tmp.x>=size || tmp.y<0 || tmp.y>=size)			//判断移动后的位置是否任在棋盘内部
				continue;

			if( false==visited[tmp.x][tmp.y]){
				
				id++;
				tmp.parent=curr.myindex;
				tmp.myindex=id;
				Queue.push(tmp);

				NodeTable[id]=new Node();
				*NodeTable[id]=tmp;

				visited[tmp.x][tmp.y]=true;
				
			}
			else
				continue;
		}
	}
	return;
}

void output(){

	stack<Node> Stack;
	Node now=End;
	Stack.push(now);

	while(now.parent!=-1){				

		Stack.push(*NodeTable[now.parent]);			//通过栈来回溯出路径
		now=Stack.top();
		minStep++;
	}

	cout<<"minStep="<<minStep<<endl;
	cout<<"移动的坐标为:"<<endl;

	while(NOT Stack.empty()){				

		cout<<"("<<(Stack.top()).x<<","<<(Stack.top()).y<<")"<<endl;
		Stack.pop();
	}
}

int main(){

	cout<<"input size、start point 、end point:"<<endl;
	cin>>size>>startX>>startY>>endX>>endY;
	init();
	clock_t time=clock();
	BFS(); 
	cout<<"计算用时: "<<clock()-time<<"MS"<<endl;
	output();
	
}

 通过这道题目我有如下的几点总结:

1、一开始我通过struct结构来表示每一步到达的坐标位置,其中设置了一个struct* parent的指针,而不是改进后的int parent; 但是在实际的调试过程中,出现了非常奇怪的现象,被压入到Queue队列中的节点的parent指针域不断地变动,最后发现原来是curr节点每次都变动的问题,而tmp节点的parent指针始终指向的是curr节点的。

 

~~~~~~~所以最后干脆使用int parent,省去指针的隐蔽错误!!~~~~~~~~~~~~~~~~~

while(NOT Queue.empty() && NOT Finish ){

 

  Node curr=Queue.front();                              //每次重新扩展节点时,curr发生了变化
  Queue.pop();
  
  if( endX==curr.x && endY==curr.y){
   Finish=true;
   End.parent=curr.parent;
   Node* t=End.parent;
   while( t!=NULL){
    minStep++;
    t=t->parent;
   }
   cout<<"mimStep="<<minStep<<endl; 
   continue;
  }
  for(k=0;k<8;k++){

   Node tmp=curr;
   tmp.x=curr.x+direction[k][0];
   tmp.y=curr.y+direction[k][1];

   if( tmp.x<0 || tmp.x>=size || tmp.y<0 || tmp.y>=size)   //判断移动后的位置是否任在棋盘内部
    continue;

   if( false==visited[tmp.x][tmp.y]){
    
    tmp.parent=&curr;                                                   //tmp节点的parent域始终指向的是curr
  Queue.push(tmp);
    visited[tmp.x][tmp.y]=true;
    
   }
   else
    continue;
  }

2、一开始发生错误的时候,不要急着直接按原问题的规模来调试,可以先用小规模问题来测试,这样往往更容易发现程序到底错在哪里。

3、上面程序可能存在的不足就是NodeTable占用了比较大的空间,不过也不会很大,因为一旦搜索到目标节点就停止了。这里使用数组其实主要是起到还原路径的作用。

4、在上面的程序中:

声明了:

PNode NodeTable[MAXSIZE*MAXSIZE]={NULL};  //存储节点的表

而在实际向表中插入数据的时候,必须先通过new操作符创建内存空间,然后进行赋值:

NodeTable[id]=new Node();       //注意一定要先new分配内存空间,再进行赋值
 *NodeTable[id]=start;

不能直接用: *NodeTable[id]=start;

 

双向广度搜索算法

 

主要的算法思想是这样的:

有些问题按照广度优先搜索法则扩展结点的规则,既适合顺序,也适合逆序,于是我们考虑在寻找目标结点或路径的搜索过程中,初始结点向目标结点和目标结点向初始结点同时进行扩展—,直至在两个扩展方向上出现同一个子结点,搜索结束,这就是双向搜索过程。出现的这个同一子结点,我们称为相交点,如果确实存在一条从初始结点到目标结点的最佳路径,那么按双向搜索进行搜索必然会在某层出现“相交”,即有相交点,初始结点一相交点一目标结点所形成的一条路径即是所求路径。

 

理论上双向BFS可以在时间和空间上做到单向BFS的 1/2 次方。所以速度比单向的bfs快一点。

 

 下面是poj1915的另一个程序版本:双向广度搜索

/*
 *	POJ1915
 *	使用双向广度搜索最佳路径
 *	双向广度搜索算法有两种形式:1、交替搜索 2、选择节点个数较少的方向扩展
 *	
 *	下面的程序使用交替搜索策略
*/

#include<iostream>
#include<queue>
#include<cstring>
#include<ctime>
using namespace std;

#define NOT !
#define MAXSIZE 801

int visited[MAXSIZE][MAXSIZE];		//0:未访问 1:前向BFS访问 2:后向BFS访问
typedef struct Node{

	int x;
	int y;
	int parent;
	int myindex;
}*PNode;
const int direction[8][2]={{-2,-1},{-1,-2},{-2,1},{-1,2},{1,-2},{2,-1},{1,2},{2,1}};		//骑士可能的前进方向
queue<Node> front,back;				//两个方向上搜索的队列
Node org,dest,half1,half2;						//起始节点和目标节点,相交节点 
int size,startX,startY,destX,destY;
int minStep;						//记录最短路径
PNode NodeTable[MAXSIZE*MAXSIZE]={NULL};	//记录查找路径过程中的节点
static int id;


/*
 *	记录查找过程中的节点
*/

void InsertNodeTable(int id,Node n){

	NodeTable[id]=new Node();
	*NodeTable[id]=n;
}
/*
 *	输入程序数据并进行初始化
*/
void input(){

	cout<<"input size、start position、end position:"<<endl;
	cin>>size>>startX>>startY>>destX>>destY;
	

	org.x=startX;
	org.y=startY;
	org.parent=-1;
	dest.x=destX;
	dest.y=destY;
	dest.parent=-1;

	memset((void*)visited,0,sizeof(visited));
	visited[startX][startY]=1;
	id=0;
	org.myindex=id;
	InsertNodeTable(id,org);
	visited[destX][destY]=2;
	id=1;
	org.myindex=id;
	InsertNodeTable(id,dest);

	front.push(org);
	back.push(dest);

}

/*
 *	判断移动后的位置是否合法
*/
inline bool isOk(int x,int y){

	if( x<0 || x>=size || y<0 || y>=size)
		return false;
	else
		return true;
}

/*
 *	双向广度搜索
*/
void Bi_bfs(){

	bool finish=false;
	Node curr,tmp;
	int i,j,frontSize,backSize;
	int n1=0,n2=0;

	while( NOT finish){

		frontSize=front.size();
		for(j=0;j<frontSize;j++)					//每次交替扩展一层
		{
			curr=front.front();
			front.pop();

			for(i=0; i<8; i++)
			{
				tmp.x=curr.x+direction[i][0];
				tmp.y=curr.y+direction[i][1];

				if(NOT isOk(tmp.x,tmp.y))
					continue;
				else if( 0 == visited[tmp.x][tmp.y]){	//未访问节点

					tmp.parent=curr.myindex;			//向NodeTable中插入节点
					id++;
					tmp.myindex=id;
					InsertNodeTable(id,tmp);

					front.push(tmp);
					visited[tmp.x][tmp.y]=1;
				}
				else if( 1 == visited[tmp.x][tmp.y])	//forward访问过的节点
					continue;
				else{									//backward访问过的节点(找到相交节点)

					half1.parent=tmp.parent;			//记录相交节点half1,还原 2方向上的路径 
					half2.parent = curr.myindex ;		//记录相交节点half2,还原 1方向上的路径 

					finish=true;
					break;
				}
			}
			if(finish) break;
		}
		n1++;
		//找到相交节点 
		if( finish){

			minStep=n1+n2;
			return;
		}

		backSize=back.size();
		for(j=0;j<backSize;j++){

			curr=back.front();
			back.pop();

			for(i=0; i<8; i++)
			{
				tmp.x=curr.x+direction[i][0];
				tmp.y=curr.y+direction[i][1];

				if(NOT isOk(tmp.x,tmp.y))
					continue;
				else if( 0 == visited[tmp.x][tmp.y]){	//未访问节点

					tmp.parent=curr.myindex;			//向NodeTable中插入节点
					id++;
					tmp.myindex=id;
					InsertNodeTable(id,tmp);

					back.push(tmp);
					visited[tmp.x][tmp.y]=2;
				}
				else if( 2 == visited[tmp.x][tmp.y])	//forward访问过的节点
					continue;
				else{									//backward访问过的节点(找到相交节点)

					half1.parent=tmp.parent;				//记录相交节点
					half2.parent=curr.myindex ;  
					
					finish=true;
					break;
				}
			}
			if( finish) break;
		}
		n2++;
		if( finish){

			minStep=n1+n2;
			return;
		}
	}
}

/*
 *	输出找到的最优路径
*/
void output(){



}


int main(){

	input();
	clock_t time=clock();
	Bi_bfs();
	cout<<"计算用时: "<<clock()-time<<"MS"<<endl;
	cout<<"minStep="<<minStep<<endl;
	return 0;

}


面总结一些双向广度搜索的特点:

1、因为是在两个方向上,同时逆向的搜索,所以需要维护两个数据结构是两个个OPEN表和CLOSE表.因为每个方向上的搜索都要对搜索过程进行记录。这里OPEN表就是一般BFS中的队列数据结构,具有先进先出的特点,而CLOSED列表就相当于对已检测节点做标记的记录数组。

2、在双向广度搜索中,可以交替的扩展每一个搜索方向上的节点,当然如果进一步优化:可以选择扩展节点个数较少的方向。

3、在扩展过程中对访问数组使用1、2进行标记两个不同方向上搜索到的节点,另外每次扩展的时候,都是把当前层的节点全部扩展完,这样再递增搜索深度的变量你n1、n2,这样最后总的搜索最优步数就是 n1+n2 。如果要还原路径,只要在两个方向上分别记录相交节点half1、half2就可以了。

下面是POJ2046 Gap的题目,主要的方法还是使用广度优先搜索,下面用三种方法进行了实现:第一种使用set容器进行状态的判重,第二种方法使用hash进行判重,第三种方法使用A*算法+hash进行判重。

/*
 *	POJ 2046
 *	解题思路:
 *	1、搜索策略:BFS
 *	2、状态描述:通过string来表示每一个状态,这样搜索的效率会更高,很有启发性 
 *	3、由于在搜索过程中是存在重复状态的,这里使用set来完成重复状态的检测
 *		这样的算法复杂度为log(n),比使用hash检测差一点O(1) 
*/

#include<iostream>
#include<fstream>
#include<string>
#include<ctime>
#include<queue>
#include<set>
using namespace std;

#define NOT !


typedef struct map{						//描述状态的结构体 
	int step;
	string state;	
}Map;

Map org,dest;							//起始状态和目标状态 
queue<Map> Queue;						//使用全局的数据结构比使用局部的数据结构效率要高 
set<string> HashSet;

/*
 *	print测试函数 
*/
void print(string state){
	
	for(int i=0,count=1;i<64; i=i+2,count++){
		
		cout<<state[i]<<state[i+1]<<" ";
		if( 8 == count ){
			
			cout<<endl;
			count=0;
		}
	}
} 

/*
 *	在状态中搜索指定的数字的位置 
*/
int stateSearchNum(string state,char a,char b){
	
	for(int i=0 ; i< 64 ; i+=2){
		
		if( state[i]== a && state[i+1] == b ){
			
			return i;
		}
	}
}

/*
 *	BFS搜索的核心函数 
*/
void bfsSearch(int& minStep){
	
	while( NOT Queue.empty()) Queue.pop();
	HashSet.clear();
	bool finish=false;
	Map tmpMap,tmpGen;
	int pos,startPos,index;
	string num="";
	
	//step1: put the root state into the queue
	Queue.push(org);
	HashSet.insert(org.state);
	
	while( NOT Queue.empty() && NOT finish){
		
		tmpMap= Queue.front();
		Queue.pop();
		startPos=0;
		
		//step2: check whether reaching destination state
		if( tmpMap.state == dest.state){
			
			finish=true;
		 	minStep = tmpMap.step;
			continue;
		}
		
		//step3: generate new states
		for(int i=0 ; i<4 ; i++){
			
			pos= tmpMap.state.find_first_of('+',startPos);
			//如果左边的是空格,或者是面值为7的牌 
			if( '+' == tmpMap.state[pos-1] || '7' == tmpMap.state[pos-1]){
				
				startPos=pos+2;							//注意,更新下次搜索开始的位置 
				continue;	
			}
			
			tmpGen=tmpMap;
			tmpGen.state[pos] = tmpMap.state[pos-2];
			tmpGen.state[pos+1] = tmpMap.state[pos-1]+1;
			
			index=stateSearchNum(tmpMap.state,tmpGen.state[pos],tmpGen.state[pos+1]);
			tmpGen.state[index]=tmpGen.state[index+1]='+';
			tmpGen.step= tmpMap.step+1;							//步数加1 
			
			if( HashSet.find(tmpGen.state) == HashSet.end() ){	//检查是否为重复状态 
				
				HashSet.insert(tmpGen.state);
				Queue.push(tmpGen);
			}
			
			startPos = pos+2;
		}
	}
}

int main(){
	
	ifstream in("test.txt");
	int testCase;
	int index;
	string tmp;
	int minStep;
	
	//初始化状态,这里用"+"表示空格 
	org.step=0;
	dest.state= "11121314151617++21222324252627++31323334353637++41424344454647++";
	
	
	org.state=dest.state;
	
	//在每行的开头位置留出空格
	org.state[0]=org.state[1]=org.state[16]=org.state[17]=org.state[32] =org.state[33]
		=org.state[48]=org.state[49]='+'; 
		
	in>>testCase;
	
	while(testCase--) {
		
		for(int i=0;i<4;i++){
			
			for(int j=1;j<8;j++){				//注意这里j应该从1开始 
				
				in>>tmp;
				
				
				//对初始状态做预处理
				if( '1' == tmp[1]) {
					
					index=(tmp[0]-'0'-1)*16 ; 
					org.state[ index ] = tmp[0];
					org.state[ index +1 ] = tmp[1];
					index=i*16+j*2;
					org.state[ index  ] = org.state[ index+1 ]='+';
				}
				else{
					
					index=i*16+j*2;
					org.state[ index ]=tmp[0];
					org.state[ index+1 ]=tmp[1];
					
				}
				
			}
		}
		minStep=INT_MAX;
		clock_t time=clock();
		bfsSearch( minStep) ;
		cout<<"计算用时: "<<clock()-time<<"MS"<<endl;
		if( INT_MAX == minStep  )
			cout<<"-1"<<endl;
		else
			cout<<minStep<<endl;

	}
	
	
	return 0;
}


 

这里体现了一个搜索优化的方法:将局面的状态转化为字符串进行状态判重。另外这里比较巧妙的是使用set容器进行状态的判重,set在这里就是hash表的作用。

/*
 *	POJ 2046
 *	解题思路:
 *	1、搜索策略:BFS
 *	2、状态描述:通过string来表示每一个状态,这样搜索的效率会更高,很有启发性 
 *	3、使用hash函数进行状态的判重 
*/

#include<iostream>
#include<fstream>
#include<string>
#include<ctime>
#include<queue>
//#include<cstdlib> 
//#include<set>
using namespace std;

#define NOT !
#define HashTableSize 262147

typedef struct map{						//描述状态的结构体 
	int step;
	string state;	
	
}Map;

Map org,dest;							//起始状态和目标状态 
queue<Map> Queue;						//使用全局的数据结构比使用局部的数据结构效率要高 



enum State{ ACTIVE,EMPTY};				//用于描述hash表中单元状态 
typedef struct entry{
	
	string state;
	State info;
	entry():info(EMPTY){}
	entry(State in=EMPTY , string str):info(in) , state(str){}
	
}Entry;
Entry HashTable[HashTableSize];			//hash表,调用默认的无参数的构造函数 
static int countState=0;
static int conflict=0;
/*
 *	print测试函数 
*/
void print(string state){
	
	for(int i=0,count=1;i<64; i=i+2,count++){
		
		cout<<state[i]<<state[i+1]<<" ";
		if( 8 == count ){
			
			cout<<endl;
			count=0;
		}
	}
} 


/*
 *	计算状态字符串的hash值 
*/
int getHashValue(string str){
	
	unsigned long value=0;
	
	for(int i=0 ; i<64 ;i++)				//对string中的每个字符进行了全部hash出来 
		value= value*37+str[i];	
		
	if( value < 0)
		value += HashTableSize ;
	
	return value%HashTableSize;
} 

/*
 *	插入hash表函数 
*/
bool insertHashTable(string str){
	
	int hashValue= getHashValue(str);
	
	if( EMPTY == HashTable[hashValue].info  ){
		
		HashTable[hashValue] = Entry(ACTIVE,str);
		countState++;
		return true;
	}
	else{
		
		if( HashTable[hashValue].state == str){			//如果是重复的状态 
			
			return false;
		}		
			
			
		else{											//如果是状态发生冲突 
			
//			srand(time(NULL));
//			unsigned int offset=rand();
//			for( ; ACTIVE == HashTable[(hashValue + offset)% HashTableSize].info ; offset = rand());
			
			int currentPos= hashValue ;
			int offset=1;
			for( ; ACTIVE == HashTable[currentPos].info ; ){
				
				currentPos+=offset;						//二次试探 
				offset+=2;	
				if( currentPos >= HashTableSize)
					currentPos %= HashTableSize;
			}
			
			HashTable[hashValue] = Entry(ACTIVE,str);
			countState++;
			conflict++;
			return true;

		}
	}
}

/*
 *	在状态中搜索指定的数字的位置 
*/
int stateSearchNum(string state,char a,char b){
	
	for(int i=0 ; i< 64 ; i+=2){
		
		if( state[i]== a && state[i+1] == b ){
			
			return i;
		}
	}
}

/*
 *	BFS搜索的核心函数 
 *	 
*/
void bfsSearch(int& minStep){
	
	while( NOT Queue.empty()) Queue.pop();
	Map tmpMap,tmpGen;
	int pos,startPos,index;
	
	//step1: put the root state into the queue
	Queue.push(org);
	insertHashTable(org.state);
	
	while( NOT Queue.empty()){
		
		tmpMap= Queue.front();
		Queue.pop();
		startPos=0;
		
		//step2: check whether reaching destination state
		if( tmpMap.state == dest.state){
			
		 	minStep = tmpMap.step;
			return;
		}
		
		//step3: generate new states
		for(int i=0 ; i<4 ; i++){
			
			pos= tmpMap.state.find_first_of('+',startPos);
			//如果左边的是空格,或者是面值为7的牌 
			if( '+' == tmpMap.state[pos-1] || '7' == tmpMap.state[pos-1]){
				
				startPos=pos+2;							//注意,更新下次搜索开始的位置 
				continue;	
			}
			
			tmpGen=tmpMap;
			tmpGen.state[pos] = tmpMap.state[pos-2];
			tmpGen.state[pos+1] = tmpMap.state[pos-1]+1;
			
			index=stateSearchNum(tmpMap.state,tmpGen.state[pos],tmpGen.state[pos+1]);
			tmpGen.state[index]=tmpGen.state[index+1]='+';
			tmpGen.step= tmpMap.step+1;							//步数加1 
			
			
			if( insertHashTable(tmpGen.state)){
				
				//step4: push new states into Queue
				Queue.push(tmpGen);
			}

			
			
			startPos = pos+2;
		}
	}
}

int main(){
	
	ifstream in("test.txt");
	int testCase;
	int index;
	string tmp;
	int minStep;
	
	//初始化状态,这里用"+"表示空格 
	org.step=0;
	dest.state= "11121314151617++21222324252627++31323334353637++41424344454647++";
	org.state=dest.state;
	
	//在每行的开头位置留出空格
	org.state[0]=org.state[1]=org.state[16]=org.state[17]=org.state[32] =org.state[33]
		=org.state[48]=org.state[49]='+'; 
		
	in>>testCase;
	
	while(testCase--) {
		
		for(int i=0;i<4;i++){
			
			for(int j=1;j<8;j++){				//注意这里j应该从1开始 
				
				in>>tmp;
				
				
				//对初始状态做预处理
				if( '1' == tmp[1]) {
					
					index=(tmp[0]-'0'-1)*16 ; 
					org.state[ index ] = tmp[0];
					org.state[ index +1 ] = tmp[1];
					index=i*16+j*2;
					org.state[ index  ] = org.state[ index+1 ]='+';
				}
				else{
					
					index=i*16+j*2;
					org.state[ index ]=tmp[0];
					org.state[ index+1 ]=tmp[1];
					
				}
				
			}
		}
		minStep=INT_MAX;
		clock_t time=clock();
		bfsSearch( minStep) ;
		
		cout<<"计算用时: "<<clock()-time<<"MS"<<endl;
		cout<<"countState = "<<countState<<endl;
		cout<<"conflict = "<<conflict<<endl;
		if( INT_MAX == minStep  )
			cout<<"-1"<<endl;
		else
			cout<<minStep<<endl;

	}
	
	return 0;
}


 

使用hash表进行状态判重。主要注意的获得hash值函数的写法(字符串hash法)。

这里的特点是,通过在hash表单元的状态empty、active来方便hash表插入元素是否重复的判断。

/*
 *	POJ 2046
 *	解题思路:
 *	1、搜索策略:A*算法 
 *	2、状态描述:通过string来表示每一个状态,这样搜索的效率会更高,很有启发性 
 *	3、使用hash函数进行状态的判重 
*/

#include<iostream>
#include<fstream>
#include<string>
#include<ctime>
#include<queue>

using namespace std;

#define NOT !
#define HashTableSize 262147

typedef struct map{						//描述状态的结构体 
	int step;
	string state;	
	int fCost;							//fCost=gCost+hCost
	int gCost;
	friend bool operator<(const map& a,const map& b){
		
		return a.fCost>= b.fCost;
	}
}Map;

Map org,dest;							//起始状态和目标状态 
priority_queue<Map> Heap;				//使用全局的数据结构比使用局部的数据结构效率要高 



enum State{ ACTIVE,EMPTY};				//用于描述hash表中单元状态 
typedef struct entry{
	
	string state;
	State info;
	entry():info(EMPTY){}
	entry(State in=EMPTY , string str):info(in) , state(str){}
	
}Entry;
Entry ClosedHashTable[HashTableSize];			//关闭hash表

static int countState=0;
static int conflict=0;
/*
 *	print测试函数 
*/
void print(string state){
	
	for(int i=0,count=1;i<64; i=i+2,count++){
		
		cout<<state[i]<<state[i+1]<<" ";
		if( 8 == count ){
			
			cout<<endl;
			count=0;
		}
	}
} 


/*
 *	计算状态字符串的hash值 
*/
int getHashValue(string str){
	
	unsigned int value=0;
	
	for(int i=0 ; i<64 ;i++)				//对string中的每个字符进行了全部hash出来 
		value= value*37+str[i];	
	
	value = value%HashTableSize;
	
	if( value <0 )
		value += HashTableSize ;
		
	return value ;
} 


bool inClosedHashTable(string str){
	
	unsigned long hashValue= getHashValue(str);
	
	if( EMPTY == ClosedHashTable[hashValue].info  ){
		
		return false;
	}
	else{
		
		if( ClosedHashTable[hashValue].state == str){		
			
			return true;
		}
	}		

	return false ;
}

/*
 *	插入关闭hash表函数 
*/
bool insertClosedHashTable(string str){
	
	unsigned long hashValue= getHashValue(str);
	
	if( EMPTY == ClosedHashTable[hashValue].info  ){
		
		ClosedHashTable[hashValue] = Entry(ACTIVE,str);
		countState++;
		return true;
	}
	else{
		
		if( ClosedHashTable[hashValue].state == str){			//如果是重复的状态 
			
			return false;
		}		
			
			
		else{											//如果是状态发生冲突 
			
//			srand(time(NULL));
//			unsigned int offset=rand();
//			for( ; ACTIVE == HashTable[(hashValue + offset)% HashTableSize].info ; offset = rand());
			
			unsigned int currentPos= hashValue ;
			unsigned int offset=1;
			for( ; ACTIVE == ClosedHashTable[currentPos].info ; ){
				
				currentPos+=offset;						//二次试探 
				offset+=2;	
				if( currentPos >= HashTableSize)
					currentPos %= HashTableSize;
			}
			
			ClosedHashTable[hashValue] = Entry(ACTIVE,str);
			countState++;
			conflict++;
			return true;

		}
	}
}



/*
 *	在状态中搜索指定的数字的位置 
*/
int stateSearchNum(string state,char a,char b){
	
	for(int i=0 ; i< 64 ; i+=2){
		
		if( state[i]== a && state[i+1] == b ){
			
			return i;
		}
	}
}

/*	
 *	估价函数
 *	计算出不在原位置的牌的数量 
*/
int getCost(string str){
	
	int cost=0;
	for( int i=0 ; i<64 ; i++){
		
		if( dest.state[i] != str[i] )
			cost++; 
	}
	
	return cost/2;
}

/*
 *	BFS搜索的核心函数 
 *	改进版本:A*算法 
*/
void bfsSearch(int& minStep){
	
	Map tmpMap,tmpGen;
	int pos,startPos,index;
	
	//step1: put the root state into the heap
	Heap.push(org);

	
	while( NOT Heap.empty()){
		
		tmpMap= Heap.top();
		Heap.pop();
		startPos=0;
		
		//进入关闭列表 
		if( !insertClosedHashTable(tmpMap.state) ){
			
			continue;
		}
		
		//step2: check whether reaching destination state
		if( tmpMap.state == dest.state){
			
		 	minStep = tmpMap.step;
			return;
		}
		
		
		//step3: generate new states
		for(int i=0 ; i<4 ; i++){
			
			pos= tmpMap.state.find_first_of('+',startPos);
			//如果左边的是空格,或者是面值为7的牌 
			if( '+' == tmpMap.state[pos-1] || '7' == tmpMap.state[pos-1]){
				
				startPos=pos+2;							//注意,更新下次搜索开始的位置 
				continue;	
			}
			
			tmpGen=tmpMap;
			tmpGen.state[pos] = tmpMap.state[pos-2];
			tmpGen.state[pos+1] = tmpMap.state[pos-1]+1;
			
			index=stateSearchNum(tmpMap.state,tmpGen.state[pos],tmpGen.state[pos+1]);
			tmpGen.state[index]=tmpGen.state[index+1]='+';
			tmpGen.step= tmpMap.step+1;							//步数加1 
			
			//检查是否为已关闭状态 
			if( inClosedHashTable(tmpGen.state) ){
				
				continue;
			}			
			
			//计算代价
			tmpGen.gCost=tmpMap.gCost+1;
			tmpGen.fCost=tmpGen.gCost+ getCost(tmpGen.state) ;
			
			//step4:push into the heap
			Heap.push(tmpGen);
			
			startPos = pos+2;
		}
	}
}

int main(){
	
	ifstream in("test.txt");
	int testCase;
	int index;
	string tmp;
	int minStep;
	
	//初始化状态,这里用"+"表示空格 
	org.step=0;
	dest.state= "11121314151617++21222324252627++31323334353637++41424344454647++";
	org.state=dest.state;
	org.fCost=org.gCost=0;

	
	//在每行的开头位置留出空格
	org.state[0]=org.state[1]=org.state[16]=org.state[17]=org.state[32] =org.state[33]
		=org.state[48]=org.state[49]='+'; 
		
	in>>testCase;
	
	while(testCase--) {
		
		for(int i=0;i<4;i++){
			
			for(int j=1;j<8;j++){				//注意这里j应该从1开始 
				
				in>>tmp;
				
				
				//对初始状态做预处理
				if( '1' == tmp[1]) {
					
					index=(tmp[0]-'0'-1)*16 ; 
					org.state[ index ] = tmp[0];
					org.state[ index +1 ] = tmp[1];
					index=i*16+j*2;
					org.state[ index  ] = org.state[ index+1 ]='+';
				}
				else{
					
					index=i*16+j*2;
					org.state[ index ]=tmp[0];
					org.state[ index+1 ]=tmp[1];
					
				}
				
			}
		}
		minStep=INT_MAX;
		clock_t time=clock();
		bfsSearch( minStep) ;
		
		cout<<"计算用时: "<<clock()-time<<"MS"<<endl;
		cout<<"countState = "<<countState<<endl;
		cout<<"conflict = "<<conflict<<endl;
		if( INT_MAX == minStep  )
			cout<<"-1"<<endl;
		else
			cout<<minStep<<endl;

	}
	
	return 0;
}

使用A*算法要注意不应该将已经进入关闭列表的元素再次加入到开放列表中。而这一般可以通过把hash表作为关闭列表来使用。(但是由于hash存在冲突的可能性,可能会把已经进入关闭列表的元素又加入到开放列表中,所以使用set容器就避免了这一点)。

转载文章出处:http://blog.csdn.net/urecvbnkuhbh_54245df/article/details/5742048

原文地址:https://www.cnblogs.com/yangzhi/p/3576641.html