Files
python/TangDou/AcWing/AStar/178_AStar_Normal_FeiQi.cpp
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

121 lines
4.4 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
//代码太过繁琐,准备放弃PII教学,全面采用Struct,就是最开始费劲点,一旦明白后,自定义排序、多参数值等比PII灵活太多
typedef pair<int, int> PII;
typedef pair<int, PII> PIII;
const int N = 1010;
const int M = 200010; //因为要建反向边,所以边数是2倍,20W+10
int n, m; //点数和边数
int S, T, K; //起点,终点,第K短
int h[N], rh[N]; //正向边的邻接表表头和反向边的邻接表表头
int e[M], w[M], ne[M], idx; //数组模拟邻接表
int dist[N]; //到每个点的最短距离
bool st[N]; //每个点用没用过
int cnt[N];
//邻接表模板
void add(int h[], int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
// 计算出点T到每个其它点的最短距离,把这个最短距离dist[i]做为估价函数
void dijkstra() {
priority_queue<PII, vector<PII>, greater<PII>> q;
q.push({0, T});
//初始化距离
memset(dist, 0x3f, sizeof dist);
dist[T] = 0;
while (q.size()) {
PII t = q.top();
q.pop();
int u = t.second; //哪个点
if (st[u]) continue; //如果这个点已经出过队列了,根据Dijkstra的理论,这个最小路径已经产生,不需要再讨论
st[u] = true; //标识已搜索
for (int i = rh[u]; ~i; i = ne[i]) { //在反向图上遍历
int j = e[i];
if (dist[j] > dist[u] + w[i]) {
dist[j] = dist[u] + w[i];
q.push({dist[j], j});
}
}
}
}
// A*算法,不用判重
int astar() {
//小顶堆,对F值进行从小到大排序
priority_queue<PIII, vector<PIII>, greater<PIII>> q;
// 小顶堆内的元素:
// PII的first: F = G + H
// G = 起点到当前点的距离
// H = 当前点到终点的估算成本
// 按实际距离累加和+估算值,按这个值来确定处理优先级
q.push({dist[S], {0, S}}); //在添加S节点时,由于S距离自己的距离是0,所以就只有一个H值,即dist[S]
//后面数对PII的含义是:当前节点距离出发点的距离,当前节点号
while (q.size()) {
PIII t = q.top();
q.pop();
// t.x在下面的代码中未使用,原因很简单,这个x本来也不是啥准确值,只是一个估值
int u = t.second.second; // 要扩展的点u
int d = t.second.first; // u 距离出发点的距离
cnt[u]++; // u 出队的次数+1
//如果此时的u就是终点T,前且已经出队k次 返回答案
if (u == T && cnt[u] == K) return d;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
/*
A*寻路的一个优化:
如果走到一个中间点都cnt[j]>=K,则说明j已经出队>=k次了,且astar()并没有return distance,
说明从j出发找不到第k短路(让终点出队k次),即继续让j入队的话依然无解,没必要让j继续入队
distance + w[i] 累加长度
真实值 + 估计值 由小到大 排序
如果当前点出队超过k次的话 用它当前权值更新的其他点必然也是大于第k次的点 所以不需要更新
*/
if (cnt[j] < K)
q.push({d + w[i] + dist[j], {d + w[i], j}});
}
}
// 终点没有被访问k次
return -1;
}
int main() {
//加快读入
cin.tie(0), ios::sync_with_stdio(false);
cin >> n >> m;
memset(h, -1, sizeof h); //正向边表头
memset(rh, -1, sizeof rh); //反向边表头
while (m--) {
int a, b, c;
cin >> a >> b >> c;
add(h, a, b, c); //正向建图
add(rh, b, a, c); //反向建图
}
cin >> S >> T >> K;
if (S == T) K++; //最短路中至少要包含一条边,如果S和T相等,那么我们不能直接从S得到T,所以起码需要走一条边,求第K+1短就可以了
//先执行一遍dijkstra算法,求出终点到每个点的最短距离,作为估值
dijkstra();
// A*寻路求第K短长度
printf("%d\n", astar());
return 0;
}