帮同学写的八数码,启发式搜索
创建两个表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 }
时间: 2024-10-26 00:06:18