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