这篇文章为洛谷P5507机关的题解
这是道很好的搜索练手题
可以用广为人知的搜索三巨头求解
这篇题解主要介绍用 A_star 算法 AC 这道题
1. 思路
用 结构体 记录 路径 状态
以及实际步数 g(n) 和估价步数 h(n)
我们都知道 A* 算法的核心在于其估价函数 h(n)
每次取出对应 g(n) + h(n) 最小的 进行下一步搜素
引出一个疑惑 :
每改变一个机关的状态都会有另一个机关跟着改变
那么估价函数如何求解 ?
看作是两个一起变换
可以 用当前状态距离目标状态的理想差值除去 2 得到
2.解法
使用优先队列存储结构体
所以我们需要 重载运算符 :
1 2 3
| bool operator<(const node &a,const node &b){ return a.g+a.h>b.g+b.h; }
|
经检验 :
由于看作两个机关同时向着目标前进 过于理想
此题的估价函数可以稍微调高一些,用于优化时间复杂度
参数为原估价函数的 1.3 倍
但此时的时间复杂度仍高了些
所以最后加上 map 去重 即可
3. Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| #include<bits/stdc++.h> using namespace std; struct node{ string state; string path; int g; int h; }; map<string,bool> mark;
bool operator<(const node &a,const node &b){ return a.g+a.h>b.g+b.h; } priority_queue<node> q;
int eva(string s){ int cnt=0; for(int i=1;i<=12;i++) if(s[i]!='1') cnt+=5-(int)(s[i]-48); return cnt/2*1.3; } void print(node s){ printf("%d\n",s.g); for(int i=0;i<s.path.size();i++) cout<<(int)(s.path[i]-48)<<" "; } int main(){ int gear[13][5]; node start; start.state+=(char)(48); for(int i=1;i<=12;i++) for(int j=1;j<=5;j++){ int x;scanf("%d",&x); if(j==1) start.state+=(char)(x+48); else gear[i][j-1]=x; } start.g=0; start.h=eva(start.state); if(!start.h){printf("0");return 0;} mark[start.state]=true; q.push(start); while(!q.empty()){ node a=q.top();q.pop(); for(int i=1;i<=12;i++){ int x=(int)(a.state[i]-48); int j=gear[i][x]; int y=(int)(a.state[j]-48); if(x==4) x=1; else x+=1; if(y==4) y=1; else y+=1; node s; s.state+=(char)(48); for(int u=1;u<=12;u++){ if(u==i){s.state+=(char)(x+48);continue;} if(u==j){s.state+=(char)(y+48);continue;} s.state+=a.state[u]; } if(!mark[s.state]){ s.g=a.g+1; s.h=eva(s.state); mark[s.state]=true; s.path=a.path+(char)(i+48); if(!s.h){print(s);return 0;} q.push(s); } } } return 0; }
|