#include using namespace std; const int INF = 0x3f3f3f3f; //代码太过繁琐,准备放弃PII教学,全面采用Struct,就是最开始费劲点,一旦明白后,自定义排序、多参数值等比PII灵活太多 typedef pair PII; typedef pair 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, greater> 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, greater> 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; }