You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

121 lines
4.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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;
}