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

2 years ago
#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]>=Kj>=kastar()return distance,
jk(k),jj
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;
}