A*算法概述:
采用广度优先搜索策略,在搜索过程中使用启发函数,即有大致方向的向前进虽然目标有时候不是很明确。
A*算法核心:
A*算法的关键在于启发函数,启发函数的优劣直接影响A*算法的效率。
f(n)=g(n)+h(n);
这个式子中:f(n)表示从初始状态到目标状态的估测代价。
g(n)表示从初始状态到当前状态的代价(已经确定)。
h(n)表示从当前状态到目标状态的估测代价(预测)。
其中:h(n)的好坏直接影响评估函数的好坏。
一个好的f(n)总能明确指引算法前进的方向,可以迅速的到达目标状态。
f*(n)=g*(n)+h*(n); 我们假设的从初始状态到目标状态的实际最小代价。
这个式子中:f(n)表示从初始状态到目标状态的实际代价。
g*(n)表示从初始状态到当前状态的代价(已经确定)g*(n)和g(n)是相等的。
h*(n)表示从当前状态到目标状态的实际代价。
若h(n)<=h*(n),则总能找到最优解。(当h(n)<h*(n)的时候,不可能找到一条从初始状态到达目标状态的路径。在搜索过程中使得h(n)逐渐接近h*(n),最终找到最优路径。
优点:
与广度优先搜索策略和深度优先搜索策略相比,A*算法不是盲目搜索,而是有提示的搜索。
缺点:
该算法一般要使用大量的空间用于存储已搜索过的中间状态,防止重复搜索。
用途:
从初始状态出发 =>经过一系列中间状态 =>最终到达目标状态(或者无法到达)。
该算法用于经过中间状态时候的行进策略(其中的中间状态或者由题目给出,或者在前边已经推导得出)。
IDA*算法:
迭代加深搜索算法,在搜索过程中采用估值函数,以减少不必要的搜索。
IDA*算法核心:
设置每次可达的最大深度depth,若没有到达目标状态则加深最大深度。
采用估值函数,剪掉f(n)大于depth的路径。
优点:
使用回溯方法,不用保存中间状态,大大节省了空间。
缺点:
重复搜索:回溯过程中每次depth变大都要再次从头搜索。
用途:
和A*算法大致相同。
实际上,估测函数f(n)也是剪枝的一种。
acm应用A*和IDA*算法的实例:
hdu
: 1043 ( Eight ) 采用IDA*算法
这里我在回溯过程中计算最大深度的时候不是每次加一而是有策略的进行增大(详见代码),如果是每次+1则687可见这个改进还是挺有效果的。
在编程时候官方给的数据都通过了,但是总是不能ac,发愁了半天。灵机一动,我自己编写了一个自动生成测试数据代码,找到bug居然是sweap函数,就是两个数据进行交换,一定要先自我判重,即是不是自己和自己交换。
#include <iostream>
#include <time.h>
using namespace std;
struct ED_Node{
int arr[9];
int blankPos;
}ed_node;
ED_Node tag_node = {1,2,3,4,5,6,7,8,0,8};
bool isMove[][4]= {{0,1,0,1},{0,1,1,1}, {0,1,1,0},
{1,1,0,1},{1,1,1,1}, {1,1,1,0},
{1,0,0,1}, {1,0,1,1},{1,0,1,0}};
int move_distance[] = {-3,3,-1,1};
int ED_weight[]={0,1,2,6,24,120,720,5040,40320}; //康托展开式中各项权重
int str[400000]; //记录路径(存储direction的下标)
char direction[] ="udlr";
int direc[]={-1,1,2,-2};//防止向回走
int currStep;//记录已走步数,其对应的方向存放在str中
int Max_deepth; //最大深度,不断递增
int Min_Distance;
int get_h(const ED_Node& node)
{
int current_x,current_y;
int tag_x,tag_y;
int sum_distance = 0;
for ( int i=0; i<9; ++i ){
if ( node.arr[i] != 0 ){
tag_x = (node.arr[i]-1)/3;
tag_y = (node.arr[i]-1)%3;
current_x = i/3;
current_y = i%3;
sum_distance += abs(tag_x-current_x)+abs(tag_y-current_y);
}
}
return sum_distance;
}
bool isSloved (const ED_Node& src,const ED_Node& tag)
{
int countSrcNX = 0;
int countTagNX = 0;
for ( int i=0; i<9; ++ i ){
for ( int j=0; j<i; ++j ){
if ( src .arr[i]>0 ){
countSrcNX = src .arr[j]>src.arr[i] ?
( countSrcNX+1) :countSrcNX ;
}
if ( tag .arr[i]>0 ){
countTagNX = tag.arr[j]>tag.arr[i] ?
( countTagNX+1) :countTagNX ;
}
}
}
return (countSrcNX &1) == (countTagNX&1);
}
void sweap(int& a,int &b)
{
if ( a!=b ){ //切记自我检测
a = a+b;
b = a-b;
a = a-b;
}
}
bool DFS()
{
int temp_distance = get_h(ed_node);
if ( temp_distance == 0 ){
return true;
}
int estimateDistance = temp_distance+currStep;
//cout<<"estimateDistance::"<<estimateDistance<<endl;
Min_Distance = Max_deepth>estimateDistance ? Min_Distance :
(Min_Distance<estimateDistance ? estimateDistance:Min_Distance);
if ( estimateDistance>Max_deepth ){
return false;
}
for ( int i=0; i<4; ++i ){
if ( !isMove[ed_node.blankPos][i] || !(direc[str[currStep]] +direc[i]) ){
continue;
}
str[++currStep] = i;
sweap(ed_node.arr[ed_node.blankPos],ed_node.arr[ed_node.blankPos+move_distance[i]]);
ed_node.blankPos = ed_node.blankPos+move_distance[i];
if ( DFS() ){
return true;
}
--currStep;
ed_node.blankPos = ed_node.blankPos-move_distance[i];
sweap(ed_node.arr[ed_node.blankPos],ed_node.arr[ed_node.blankPos+move_distance[i]]);
}
return false;
}
void IDAstar()
{
if ( !isSloved(ed_node,tag_node) ){
cout<<"unsolvable"<<endl;
return ;
}
currStep = 0;
Max_deepth = get_h(ed_node);
Min_Distance = Max_deepth;
while ( !DFS() ){
//Max_deepth++;
Max_deepth = Min_Distance>Max_deepth ? Min_Distance : (Max_deepth+1);
}
for (int i=1; i<=currStep; ++i) {
cout<<direction[str[i]];
}
cout<<endl;
}
int main()
{
char str [100];
//自动生成数据测试
//srand((unsigned)time(NULL));
//int a[9];
//int pos ;
//while ( true ){
//for ( int i=0; i<9 ; ++i ){
//a[i]=i;
//}
//for ( int i=9; i>1 ; --i ){
//pos = rand()%i;
cout<<pos<<" "<<i-1<<endl;
//sweap(a[pos],a[i-1]);
//}
//for ( int i=0; i<9 ; ++i ){
//cout<<a[i];
//}
//cout<<endl;
//for ( int i=0; i<9 ; ++i ){
// ed_node.arr [i] = a[i];
// if ( a[i] == 0 ){
// ed_node.blankPos = i;
// }
//}
while ( gets (str)!=NULL ){
int k =0;
for ( int i=0; i<9; ++ i,k ++ ){
if ( str [k] == 'x' ){
ed_node.arr [i] = 0;
ed_node.blankPos = i;
} else if ( str[k]>= '1' && str [k]<= '9'){
ed_node.arr [i] = str[k]-'0';
} else {
-- i;
}
}
IDAstar();
}
}
使用A*算法:可见在空间上和时间上都有明显优势(其实优势最明显的在空间,我的A*算法比较垃圾,效率不高所以时间比较长)。
#include <iostream>
#include <queue>
using namespace std;
static const int MAX_ED_SIZE = 9;
static const int MAX_ED_HASH_SIZE = 400000;
struct ED_Hash { //ED:eight diagital
int fatherHashPos ;
char direction ; //父节点到当前节点的移动方向
} ed_hash[ MAX_ED_HASH_SIZE ];
struct ED_Node {
int arr [9];
int hashPos ;
int blankPos ;
int h , g; //g:already cost h:estimate cost
bool operator < (const ED_Node& node )const {
//return h !=node.h ? h>node.h : g>node.g;
return (h+g)==(node.h+node.g)? g<node.g :(h+g)>(node.h+node.g);
}
}ed_keep, ed_temp;
int ED_weight [ MAX_ED_SIZE]={0,1,2,6,24,120,720,5040,40320}; //康托展开式中各项权重
char direction [4]={ 'l', 'r', 'u', 'd'};
int way [4][2]={{0,1},{0,-1},{1,0},{-1,0}};
bool isSloved ( const ED_Node& src ,const ED_Node& tag )
{
int countSrcNX = 0;
int countTagNX = 0;
for ( int i=0; i< MAX_ED_SIZE; ++ i ){
for ( int j=0; j< i; ++ j ){
if ( src . arr[ i]>0 ){
countSrcNX = src . arr[ j]> src. arr [ i] ?
( countSrcNX +1) :countSrcNX ;
} // end if
if ( tag . arr[ i]>0 ){
countTagNX = tag . arr[ j]> tag. arr [ i] ?
( countTagNX +1) :countTagNX ;
} //end if
} //end for
} //end for
return (countSrcNX &1) == (countTagNX&1);
}
int getHashValue ( const ED_Node& node ) //利用康托展开式,计算hash值
{
int HashValue =0;
for ( int i=1; i< 9; ++ i ){
int countNX = 0;
for ( int j=0; j< i; ++ j ){
countNX = node . arr[ j]> node. arr [ i] ?
( countNX +1) :countNX ;
}
HashValue += countNX * ED_weight[ i ];
}
return HashValue ;
}
int get_H ( const ED_Node& node ) // 获取曼哈顿距离,即估测代价
{
int currentRow , currentCol;
int tagRow , tagCol, temp;
int sumCost = 0;
for ( int i =0; i< 9; ++ i ){
//if ( node .arr[i] ){
temp = (node .arr[i]-1)%9;
currentRow = i /3;
currentCol = i %3;
tagRow = temp /3;
tagCol = temp %3;
sumCost += abs ( tagRow - currentRow ) + abs ( tagCol - currentCol );
//}
}
return sumCost ;
}
inline void swap( int& a ,int & b)
{
a ^=b ;
b ^=a ;
a ^=b ;
}
inline bool isOk (const int& x,const int& y)
{
return x >=0 && y>=0 && x<3 && y <3;
}
void ED_Astar ( ED_Node node ,const ED_Node& tag )
{
const int tagHashPos = getHashValue (tag );
int fatherHashPos ,blankPos , numberPos;
priority_queue <ED_Node > que;
que .push ( node);
while ( !que . empty() ){
ed_keep = que .top();
que .pop ();
fatherHashPos = ed_keep .hashPos;
blankPos = ed_keep .blankPos;
for ( int i=0; i<4; ++ i ){
int x = blankPos/3;
int y = blankPos%3;
x -= way [i][0];
y -= way [i][1];
if ( isOk (x,y) ){
numberPos = x *3 +y;
ed_temp = ed_keep ;
swap(ed_temp .arr[blankPos], ed_temp.arr [numberPos]);
ed_temp.hashPos = getHashValue( ed_temp);
if ( ed_hash [ed_temp. hashPos].direction == 0 ){
ed_hash[ed_temp .hashPos]. direction = direction [i] ;
ed_hash[ed_temp .hashPos]. fatherHashPos = fatherHashPos ;
ed_temp.blankPos = numberPos;
ed_temp.g ++;
ed_temp.h = get_H(ed_temp);
que.push (ed_temp);
}
if ( ed_temp .hashPos == tagHashPos ){
return ;
}
}
}
}
}
void reserveStr (char* str,const int nSize)
{
for ( int i=0; i<nSize/2; ++ i ){
char temp = str[i];
str[i ] = str[nSize-1- i];
str[nSize -1-i] = temp;
}
}
void getWay ( const ED_Node& node )
{
int hashPos = getHashValue( node );
char strRout [10000];
int i =0;
for ( ; i <10000 && hashPos!=-1; ++ i ){
strRout [i ] = ed_hash[ hashPos ].direction ;
hashPos = ed_hash [ hashPos]. fatherHashPos ;
//cout<<strRout [i];
}
//strRout [--i ] = '\0';
/*reserveStr(strRout ,i);
cout <<strRout << endl;*/
for( i -=2;i>=0; i--)
putchar(strRout [i]);
puts("");
}
void main ()
{
ED_Node src , tag;
//初始化tag
for ( int i=0; i< MAX_ED_SIZE-1; ++ i ){
tag.arr [ i] = i+1;
}
tag.arr [ MAX_ED_SIZE-1] = 0;
tag.hashPos = getHashValue ( tag );
//初始化src
char str [100];
while ( gets (str)!=NULL ){
int k =0;
for ( int i=0; i<9; ++ i,k ++ ){ //初始化src
if ( str [k] == 'x' ){
src.arr [i] = 0;
src.blankPos = i;
} else if ( str[k]>= '1' && str [k]<= '9'){
src.arr [i] = str[k]-'0';
} else {
-- i;
}
}
src .g = 0;
src .h = get_H( src);
src.hashPos = getHashValue ( src );
//判断初始值
if ( !isSloved ( src, tag) ){
cout<<"unsolvable\n";
//printf("unsolvable\n" );
continue;
} else if ( src.hashPos == tag.hashPos ){
cout<<endl;
//puts("");
continue;
}
memset (ed_hash ,0, sizeof( ED_Hash )*MAX_ED_HASH_SIZE );
ed_hash [src .hashPos ]. direction = 1 ;
ed_hash [src .hashPos ]. fatherHashPos = -1 ;
//使用A*算法
ED_Astar(src ,tag);
getWay(tag );
}
}