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