A* star 算法
第K短路
问题链接
A* star 特征:点数特别多。
此算法前置技能: Dijkstra 最短路
该算法与Dijkstra算法不同:Dijkstra按照dist[s]排序,而该算法按照dist[s] + f(s) 排序。
f(s) 为估价函数,只要 f(s) <= g(s) (g(s)为真实距离 ,
即从s到目标点的真实距离),
就可以保证 当某个状态s,第一次优先队列中出来是 他的距离 就一定是最短距离了。
如果f(s) == 0,则退化为Dijkstra算法。
t为s的下一个节点
dist[s] + f(s) <= dist[t] + f(t) <= dis[t] + g(t) + edge(t->s);
1.在宽搜(最短路)问题中, 如果所有边权都是非负的,
那么就可以使用启发函数来优化BFS过程。
2.核心思想:
(1): 建立反向图, 在反向图中求出目标点(T)到所有点的最短距离,
作为每个点的估价函数。
(2):从起点(S)开始扩展,
每次取出当前估价值最小的点,将其能扩展的点全部不扩展。(扩展就是宽搜 把能到的点全部加入优先队列)。
估计值 = 距离起点的真实距离 + 估价函数。
(3):当第K次遇到T; 就求出了从S到T的第K短路了;
下面是函数区:
#include <stdio.h>
#include <queue>
#include <string.h>
#include <string>
#include <algorithm>
using namespace std;
const int N = 1010, M = 200010;
typedef pair<int, int> PII;
typedef pair<int, PII> PIII;
int h[M], rh[M] ,e[M], en[M], w[M], cnt;
int S, T, K;
int dis[M], f[M] ,vis[M];
//建立邻接表:
void add (int *h, int a, int b, int c)
{
e[cnt] = b;
w[cnt] = c;
en[cnt] = h[a];
h[a] = cnt ++;
}
//利用该函数求目标点到每个点的最短路:
void Dijkstra()
{
priority_queue<PII, vector<PII>, greater<PII> > head;
memset(vis, 0, sizeof vis);
memset(dis, 0x3f, sizeof dis);
dis[T] = 0;
head.push({0, T});
while (head.size())
{
PII t =head.top();
head.pop();
int s = t.second;
if (vis[s]) continue;
vis[s] = 1;
for (int i = rh[s]; ~i; i = en[i])
{
int j = e[i];
if (dis[j] > dis[s] + w[i])
{
dis[j] = dis[s] + w[i];
head.push({dis[j], j});
}
}
}
memcpy(f, dis, sizeof f); //f 为估价函数 与dis值一样 复制一下w是为了更加容易理解
}
int a_star()
{
priority_queue <PIII, vector<PIII>, greater<PIII> > p;
p.push({f[S], {0, S}}); //利用估价值排序 即真实距离+估价函数
memset(vis, 0, sizeof vis);
while (p.size())
{
PIII t = p.top();
p.pop();
int ver = t.second.second, distance = t.second.first;
if (vis[ver] >= K) continue;
vis[ver] ++ ;
if (ver == T && vis[ver] == K) return distance;
for (int i = h[ver]; ~i; i = en[i])
{
int j = e[i];
if (vis[j] < K)
p.push({distance + w[i] + f[j], {distance + w[i], j}});
}
}
return -1;
}
int main ()
{
int n, m;
scanf ("%d%d", &n, &m);
memset(h, -1, sizeof h);
memset(rh, -1, sizeof rh);
for (int i = 0; i < m; i ++ )
{
int a, b, c;
scanf ("%d%d%d", &a, &b, &c);
add(rh, b, a, c); //建立反向图
add(h, a, b, c); //正向图
}
scanf ("%d%d%d", &S, &T, &K);
if (S == T) K ++ ;
Dijkstra(); //求从T到所有点的最短距离作为每个点的估价函数
printf ("%d\n", a_star());
return 0;
}