正文

A*算法求八数码问题总结 2007-03-30 22:25:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/emath/24418.html

分享到:

A*算法求八数码问题总结 最近在PKU的ACM上做了八数码问题,将经验总结以下: (1) 用A*算法, 估价函数选“Hamilton距离”比选用“在错误方各内的数字数目”强很多。 (2)A*算法的关键是要能够使“找到耗费最小的节点”和“判断是否子节点已经存在”的算法的效率尽可能高,为此,网上不少资料建议采用Binary Heap或Hash table等数据结构,然而单独使用任何一种数据结构,都有一定的缺陷,将Heap和Hash联合起来使用,应该可以提高不少效率。具体如下: 1。建一张Hash表,存放没一个节点的具体信息,如当前状态、父节点在表中的位置等。 2。open表中存放着一些节点,这些节点不同于Hash表中的节点,可以将它理解为是Hash表中节点的索引,它只包含节点的估价和节点在Hash表中的位置,这些节点按估价排列成堆(Heap)。 3。没必要再建一个closed表。 这样,程序的流程就可以写成: 初始化Hash表和open表;  将根节点放入Hash表和open表;  found = false;  while(open表不空) {      从open表中得到耗费最低的节点的索引cur;  // O(1)      从open表中删除耗费最低的节点;  // O(logN)      for 每个cur的子节点now do {           if ( now 是目标节点 ) {                   打印输出;                   found = true;                   结束搜索;           }           if( now 不在Hashtable中 ) {  // O(1)                将now插入Hashtable中;  // O(1)                将now的索引放入open表中;  // O(logN)           } else if( now比Hashtable中的节点优 ) {                  if( 原节点在open表中 ) {  // O(N)                     用now替代原来节点;  // O(1)                     在open中push_heap来更新open表的顺序;  // O(logN)                 }           }       }   }   if( !found ) 输出 "无解"; 可以看到,虽然查找open表中已存在的节点仍然为O(N), 但是当now已存在时,now比open表中节点优的概率是很小的,事先用O(1)的时间判断,就可以避免用O(N)的时间来查找,从而提高了效率(这很像是CPU的Cache的工作原理)。 具体程序如下: #include <vector>#include <string>#include <iostream>#include <algorithm>#include <cassert>using namespace std;struct OpenNode{ int cost, point; OpenNode(){} OpenNode( int cost, int point ){  this->cost = cost;  this->point = point; }};bool operator<( const OpenNode& a, const OpenNode& b ){ return a.cost > b.cost;}bool operator==(const OpenNode& a, int c){ return a.point == c;} struct HashNode{ char state[3][3]; int g, h, par, dir,x,y; bool used; HashNode(){  used=false; }}; int dx[] = { -1,0,1,0 },dy[] = { 0,1,0,-1 };  // u r d l const int HASH_SIZE = 39999;HashNode List[HASH_SIZE];int factorial[] = {1,1,2,6,24,120,720,5040,40320};/*int hash( char state[3][3] ){int ret = 0;char *p, *q;for(p=&state[0][0];p<=&state[2][2];p++){int cnt = 0;for(q=&state[0][0]; q<p; q++) if( *q<*p ) cnt++;ret += factorial[&state[2][2]-p]*(*p-cnt);}return ret;}*/bool eq( char a[3][3], char b[3][3] ){ for(int i=0;i<3;i++)  for(int j=0;j<3;j++) if( a[i][j]!=b[i][j] ) return false;  return true;}int hash( char state[3][3] ){ char* p = &state[0][0]; int ret = p[0] * 7 + p[1] * 17 + p[2] * 47 + p[3] * 117 + p[4] * 217 + p[5]   * 977 + p[6] * 1299 + p[7] * 5971 + p[8] * 7779; ret %= HASH_SIZE; while( List[ret].used && !eq(List[ret].state , state) )   ret= (ret+1) % HASH_SIZE; return ret;}int h( char state[3][3] ){ int ret = 0; for(int x=0;x<3;x++)  for(int y=0;y<3;y++) if( state[x][y]!=0 )   ret += abs((state[x][y]-1)/3-x) +abs((state[x][y]-1)%3-y);  return ret;}void output( int i ){ string res; while(List[i].dir>=0){  res+= List[i].dir==0?'u':List[i].dir==1?'r':List[i].dir==2?'d':'l';  i = List[i].par; } reverse( res.begin(), res.end() ); cout<<res<<endl;}bool solvable( char state[3][3] ){  int t = 0;  for(char* i=&state[0][0];i<&state[2][2];i++)    for(char* j=i+1;j<=&state[2][2];j++)      if(*i!=0 && *j!=0 && *i>*j) t++;  if(t%2) return false;  return true;}vector<OpenNode> open;int main(){ string init; getline( cin, init ); HashNode now; int cnt=0; for(int i=0;i<init.size();i++) {  if(init[i] == 'x') {   now.state[cnt/3][cnt%3] = 0;   now.x = cnt /3;   now.y = cnt %3;   cnt++;  } else if(init[i]!=' ') {   now.state[cnt/3][cnt%3] = init[i] - '0';   cnt++;  } } if( !solvable(now.state) ) {   cout<<"unsolvable"<<endl;   return 0; } now.g = 0; now.h = h(now.state); now.par = -1; now.dir = -1; now.used = true; int i = hash(now.state); List[i] = now; open.push_back(OpenNode(now.g+now.h,i)); while(!open.empty()){  int cur = open.front().point;  pop_heap( open.begin(), open.end() );  open.erase( open.end()-1 );  for(int i=0;i<4;i++){   now = List[cur];   int x = now.x+dx[i];   int y = now.y+dy[i];   if(x<0||x>2||y<0||y>2) continue;   swap( now.state[x][y], now.state[now.x][now.y] );   now.x = x;   now.y = y;   now.h = h( now.state );   now.g++;   now.par = cur;   now.dir = i;      int hashcode = hash( now.state );   if( now.h == 0 ){    List[hashcode] = now;    output( hashcode );    return 0;   } else if( !List[hashcode].used ){    List[hashcode] = now;    open.push_back( OpenNode(now.h+now.g,hashcode) );    push_heap( open.begin(), open.end() );   } else if( List[hashcode].g+List[hashcode].h > now.g+now.h ){    List[hashcode] = now;    vector<OpenNode>::iterator it = find( open.begin(), open.end(), hashcode );    if(it==open.end()) continue;    push_heap( open.begin(), it+1 );   }  } } cout<<"unsolvable"<<endl; return 0;}

阅读(6341) | 评论(1)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

loading...
您需要登录后才能评论,请 登录 或者 注册