A*算法理解
A*算法本质还是BFS宽度优先算法,只不过加入了评估函数h,用以衡量每个加入点的优先级。
实现方式是采用优先队列,权重值设置为:f(x) + h(x),其中f(x)代表当前实际消耗,h(x)表示预测到达终点消耗。
评估函数h(x)作为一种预测,要求小于等于实际到达终点的距离。
力扣1631 最小体力消耗路径
class Solution { public: int dir[5] = {-1, 0, 1, 0, -1}; char op[4] = {'u', 'r', 'd', 'l'}; struct Node{ int u; int maxV; bool operator <(const Node& x) const{ return maxV > x.maxV; } }; int minimumEffortPath(vector<vector<int>>& heights) { int n = heights.size(), m = heights[0].size(); priority_queue<Node> pq; pq.push({0, 0}); unordered_map<int, int> dist; unordered_map<int, pair<char, int>> path; dist[0] = 0; while(pq.size()){ auto [u, maxV] = pq.top(); pq.pop(); if(u == m * n - 1){ break; } for(int k = 0; k < 4; ++k){ int x = u / m, y = u % m; int nx = x + dir[k], ny = y + dir[k + 1]; if(nx < 0 || nx >= n || ny < 0 || ny >= m){ continue; } int tmpMax = max(maxV, abs(heights[x][y] - heights[nx][ny])); int v = nx * m + ny; if(!dist.count(v) || dist[v] > tmpMax){ dist[v] = tmpMax; pq.emplace(v, tmpMax); path[v] = {op[k], u}; } } } vector<char> pathAns; int st = m * n - 1, ed = 0; while(st != ed){ pathAns.emplace_back(path[st].first); st = path[st].second; } reverse(pathAns.begin(), pathAns.end()); for(auto c : pathAns){ cout << c << " "; } return dist[m * n - 1]; } };
八数码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<unordered_map>
#include<queue>
#define x first
#define y second
using namespace std;
typedef pair<int,string> PIS;
int f(string m)//估计函数
{
int dt=0;
for(int i=0;i<9;i++)//这里1~8对应的下标为0~7
if(m[i]!='x')
{
int t=m[i]-'1';//对应下标
dt=dt+abs(i/3-t/3)+abs(i%3-t%3);//曼哈顿距离
}
return dt;//返回总曼哈顿距离
}
string bfs(string start)
{
string end="12345678x";//终点
unordered_map<string,int> d;//存储距离
priority_queue<PIS, vector<PIS>, greater<PIS>> heap;//小根堆,将元素的估计终点距离从小到大排序
unordered_map<string,pair<string,char>> last;//存储一个元素由哪种状态,经过哪种操作得来,跟前面几题一样
heap.push({f(start),start});//加入起点
d[start]=0;//起点到起点的距离为0
//要将操作数组与坐标变化数组一一对应
char oper[]="udlr";
int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
while(heap.size())
{
auto t=heap.top();//队头
heap.pop();//弹出
string state=t.y;//记录
if(t.y==end) break;//终点出列的话就退出
int x,y;//查找x的横纵坐标
for(int i=0;i<9;i++)
if(state[i]=='x')
{
x=i/3,y=i%3;
break;
}
string init=state;
for(int i=0;i<4;i++)
{
int a=x+dx[i],b=y+dy[i];
if(a<0||a>=3||b<0||b>=3) continue;//越界就跳过
swap(state[a*3+b],state[x*3+y]);//交换下标位置
if(!d.count(state)||d[state]>d[init]+1)//如果没有被记录或者小于记录值
{
d[state]=d[init]+1;//更新距离
heap.push({f(state)+d[state],state});//加入堆中
last[state]={init,oper[i]};//标记由哪种状态转移而来,并且记录执行的操作
}
state=init;//因为要扩展到四个方向,所以要还原
}
}
string ans;
//跟前面几题原来相同
while(end!=start)
{
ans+=last[end].y;
end=last[end].x;
}
reverse(ans.begin(),ans.end());//将其反转
return ans;
}
int main()
{
string start,x,c;
while(cin>>c)//这样输入可以忽视空格
{
start+=c;
if(c!="x") x+=c;
}
int res=0;//统计逆序对的数量
for(int i=0;i<8;i++)
for(int j=i+1;j<8;j++)
if(x[i]>x[j])
res++;
if(res%2) printf("unsolvable\n");//如果逆序对为奇数,就不可能抵达终点
else cout<<bfs(start)<<endl;//输出答案
return 0;
}