zoukankan      html  css  js  c++  java
  • A*八数码

    帮同学写的八数码,启发式搜索

    创建两个表open,close,分别用的stl中的优先队列priority_queue和map,好久没写过代码了,bug调了半天

      1 #include <iostream>
      2 #include <cstdio>
      3 #include <cstring>
      4 #include <set>
      5 #include <queue>
      6 #include <algorithm>
      7 #include <vector>
      8 #include <map>
      9 #include <time.h> 
     10 using namespace std;
     11 struct Item        //每一个状态
     12 {
     13     int state[3][3];
     14     int Pre;    //父状态在path中的下标
     15     int F,G,H;    //估计函数 F=G+H
     16     Item(int state[][3],int Pre,int G,int H)    //构造函数
     17     {
     18         this->Pre=Pre;
     19         this->G=G;
     20         this->H=H;
     21         this->F=G+H;
     22         for(int i=0;i<3;i++)
     23             for(int j=0;j<3;j++)
     24                 this->state[i][j]=state[i][j];
     25     }
     26     bool operator <(const Item temp) const        //运算符重载,用于priority_queue中元素的比较
     27     {
     28         return F>temp.F;
     29     }
     30     bool operator ==(const Item temp) const        
     31     {
     32         for(int i=0;i<3;i++)
     33             for(int j=0;j<3;j++)
     34                 if(state[i][j]!=temp.state[i][j])    return 0;
     35         return 1;
     36     }
     37 };
     38 
     39 priority_queue<Item> Open;    //存储扩展出但还没有访问的表
     40 map<int,bool> Close;        //存储已经访问过的节点
     41 vector<Item> path;            //保存路径
     42 int arrays[][3]={0,1,4,2,7,6,3,8,5},arraye[][3]={1,2,3,4,5,6,7,8,0};    //八数码初始状态和目标状态
     43 int dx[]={0,0,-1,1},dy[]={1,-1,0,0};    //四个方向扩展
     44 int CalcuH(const int a0[][3],const int a1[][3])    //CalcuH(当前状态,目标状态)计算 H,如果目标状态和当前状态某个位置数字不同,dis自加1
     45 {
     46     int dis=0;
     47     for(int i=0;i<3;i++)
     48         for(int j=0;j<3;j++)
     49             if(a0[i][j]!=a1[i][j])    dis++;
     50     return dis;
     51 }
     52 bool Judge(const int p0[][3],const int p1[][3])    //两者逆序数奇偶性相等,看八数码是否有解
     53 {
     54     int ss = 0, ee = 0;
     55     for(int i=0; i<9; ++i)
     56         for(int j=0; j<i; ++j) {
     57             if(p0[j/3][j%3] != 0 && p0[j/3][j%3] < p0[i/3][i%3]) ++ss;
     58             if(p1[j/3][j%3] != 0 && p1[j/3][j%3] < p1[i/3][i%3]) ++ee;
     59         }
     60     return (ss&1) == (ee&1);    
     61 }
     62 int GetIndex(const int a[][3])        //获取hash值,将Close[t]设置为1,表示已经访问过
     63 {
     64     int t=0;
     65     for(int i=0;i<3;i++)
     66         for(int j=0;j<3;j++)
     67             t=t*10+a[i][j];
     68     return t;
     69 }
     70 int PrintPath(const Item p)            //递归打印路径
     71 {
     72     if(p.Pre==-1)
     73     {
     74         for(int i=0;i<3;i++)
     75         {
     76             for(int j=0;j<3;j++)
     77                 cout<<"    "<<p.state[i][j]<<" ";
     78             cout<<endl;
     79         }
     80         cout<<endl;
     81         return 0;
     82     }
     83     PrintPath(path[p.Pre]);
     84     cout<<p.G<<" ==>"<<endl;
     85     for(int i=0;i<3;i++)
     86     {
     87         for(int j=0;j<3;j++)
     88             cout<<"    "<<p.state[i][j]<<" ";
     89         cout<<endl;
     90     }
     91     cout<<endl;
     92     return 0;
     93 }
     94 /*search()函数中的思路:
     95     1.将初始节点放入open。(open是优先队列,f最小的节点排在队列的最前面)
     96     2.从open中取出f最小的节点p,放入到path中,如果为目标节点,则递归打印路径。否则将该状态放入到close中,并生成他的扩展节点集P(就是将空白点往四个方向移动)。
     97     3.对于扩展出的每个子节点 temp:
     98         temp.calcuf(),计算f,pre,
     99         如果temp不在close,就把它放入open中,否则不管
    100     4.回到步骤2;
    101 
    102 八数码无解情况判断:
    103     初始状态和目标状态的逆序数奇偶性相同,则可到达
    104 */
    105 void Search(Item s,int end[][3])
    106 {
    107     int x,y,mx,my;
    108     path.clear();
    109     Open.push(s);
    110     while(1)
    111     {
    112         Item e=Open.top();
    113         Open.pop();
    114         path.push_back(e);
    115         int in=GetIndex(e.state);        
    116         Close[in]=1;    //标记表示已经访问过
    117         int len=path.end()-path.begin()-1;        //获取e节点在path中的位置,他是扩展出的节点的父节点。
    118         if(CalcuH(e.state,end)==0)
    119         {
    120             //cout<<e.G<<endl;
    121             cout<<"一共需要"<<e.G<<""<<endl;
    122             PrintPath(e);
    123             return;
    124         }
    125         for(int i=0;i<3;i++)        //找到0的位置,0表示空白
    126             for(int j=0;j<3;j++)
    127                 if(e.state[i][j]==0)
    128                     x=i,y=j;
    129         for(int i=0;i<4;i++)        //向四个方向扩展
    130         {
    131             mx=x+dx[i],my=y+dy[i];
    132             if(mx>2||mx<0||my<0||my>2)    //判断是否跑出3*3的数组
    133                 continue;
    134             swap(e.state[mx][my],e.state[x][y]);    //将空白点与周围点交换位置
    135             Item temp(e.state,len,e.G+1,CalcuH(e.state,arraye));    //构造出新的状态节点
    136             swap(e.state[mx][my],e.state[x][y]);    //再交换回来
    137             int index=GetIndex(temp.state);        //获取hash值
    138             if(!Close.count(index))
    139             {
    140                 Open.push(temp);
    141             }            
    142         }
    143     }
    144     return;
    145 }
    146 int main()
    147 {
    148     clock_t t=clock(),e;
    149     Item s(arrays,-1,0,CalcuH(arrays,arraye));    
    150     if(Judge(arrays,arraye))        //判断是否有解
    151         Search(s,arraye);
    152     else
    153         cout<<"no path"<<endl;
    154     e=clock();
    155     cout<<"run time : "<<e-t<<" ms"<<endl;
    156     return 0;
    157 }
  • 相关阅读:
    机器人走方格问题
    一道数列的规律题(使用递归解决)
    反转单链表
    求一个二叉树的深度以及如何判断一个二叉树是一个平衡二叉树
    打印素数
    DAY28-mysql扩展与预处理-查出问题的关键
    DAY31
    jQuery很简单很基础的
    JavaScript中的事件委托及好处
    结合个人经历总结的前端入门方法
  • 原文地址:https://www.cnblogs.com/a1225234/p/5721431.html
Copyright © 2011-2022 走看看