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.

79 lines
3.6 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 N = 10010, M = N << 1, K = 110; // 节点个数上限,边数上限,发车间隔时长上限
const int INF = 0x3f3f3f3f;
struct Node {
int u, r, d; // u节点编号r状态d最短到达时间
// 重载 < 优先队列默认使用大顶堆我们需要小顶堆所以需要重载小于号描述两个Node对比
// 距离大的反而小,由于是大顶堆,所以距离小的在上
bool operator<(const Node t) const {
return d > t.d;
}
};
// 链式前向星
int e[M], h[N], idx, w[M], ne[M];
void add(int a, int b, int c = 0) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
/*
dis[u][i]表示到达第u处地点并且到达时间mod k = i的情况下的最短距离
*/
int dis[N][K], st[N][K];
int main() {
memset(h, -1, sizeof h); // 初始化链式前向星
int n, m, k;
cin >> n >> m >> k;
while (m--) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c); // 从a到b建一条边c为此路径的开放时间
}
// 初始状态1号点在状态0时最短距离为0其它点的最短距离为无穷大
memset(dis, 0x3f, sizeof dis); // dis是一个二维结果数组一维是节点号二维是此节点在不同状态下可以到达的最短路径
dis[1][0] = 0; // 所谓状态是指到达时间对于1号节点而言就是出发时间%k的余数值
// 因为题目中说到1号点和n号点都必须是%k=0的时间所以dis[1][0]=0
// 描述1号节点在状态0下出发距离出发点的距离是0
// 堆优化版Dijkstra求最短路注意默认大顶堆自定义比较规则
priority_queue<Node> q;
// 初始状态加入优先队列,{点,状态,最短到达时间}
q.push({1, 0, dis[1][0]}); // 节点号,%k的值已经走过的距离
// 每个进队列的节点都有3个属性节点号%k的值已经走过的距离
while (q.size()) { // 从1号点开始宽搜
int u = q.top().u; // 节点编号u
int r = q.top().r; // 节点状态r
q.pop();
if (st[u][r]) continue;
// 该状态已经加入到集合中,也就是已经被搜索过
// 先被搜索过在队列宽搜中意味着已经取得了最短路径
st[u][r] = 1;
for (int i = h[u]; ~i; i = ne[i]) { // 枚举邻接点v和连接到v节点道路的开放时间
int v = e[i]; // v节点也就是下一个节点
int t = w[i]; // v节点的开放时间
int d = dis[u][r]; // 到达(u,r)这个状态时的最短距离d
int j = (r + 1) % k; // v节点的状态应该是u节点的状态+1后再模k
// 如果到达时间小于开放时间则将到达时间向后延长若干个k的整数倍(向上取整)
while (d < t) d += k;
// 如果可以松弛到v点的时间
if (dis[v][j] > d + 1) { // 下一个节点v的j状态可以通过(u,i)进行转移那么可以用t+1更尝试更新掉dis[v][j]
dis[v][j] = d + 1;
q.push({v, j, dis[v][j]}); // 再用(v,j)入队列去更新其它节点数据标准的Dijkstra算法
}
}
}
if (dis[n][0] == INF) // 如果终点的模k=0状态存在数字那么就是说可以获取到最短路径,否则就是无法到达为个状态
cout << -1 << endl;
else
cout << dis[n][0] << endl;
return 0;
}