Files
python/TangDou/CSP-J/J-2023/bus.cpp
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

79 lines
3.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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