公路 (CSP-J 2023)
多层图DP松弛,最短路变形。
题意如下:给定一个有向图,边有开放时间,入口和出口每时间开放一次,每条边通过时间为,问从入口到出口的最短时间。根据条件,路线需要满足两个条件:
- 从入口到出口经过的整数倍条边
- 经过每条边时的起点时间大于等于这条边的开放时间
点数, 边数, 时间间隔
我们看到很小,所以从入手。把时间模,这样我们就可以把时间转换为模的余数,图变成了层图。
我们定义为从入口经过的条边,到达点的最早时间。然后答案就是。
然后用一个队列来管理活跃的点(开始为,起点),不断松弛数组就可以了。
具体就是,对于队列中的下一个点,我们遍历它的所有邻居,然后更新,我们对:
- 如果不早于边的开放时间,则,如果早于,则t是大于等于开放时间的第一个同余数。
- 如果小于,则更新为,并把加入队列。
细节见程序。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, m, k;
vector<pi> v[10005]; // edges with earliest time
int dist[10005][105]; // earliest time to reach a node, mod K
queue<int> q;
bool inq[10005];
int main() {
scanf("%d %d %d", &n, &m, &k);
for (int i = 0; i < m; i++) {
int x, y, t;
scanf("%d %d %d", &x, &y, &t);
v[x].push_back({y, t});
}
for (int i = 1; i <= n; i++)
for (int j = 0; j < k; j++)
dist[i][j] = 1e9;
dist[1][0] = 0;
q.push(1);
inq[1] = true;
while (!q.empty()) {
int x = q.front();
q.pop();
inq[x] = false;
for (pi p : v[x]) {
int y = p.first;
int t0 = p.second;
for (int i = 0; i < k; i++) {
if (dist[x][i] >= 1e9) continue;
// apply edge time limit
int t = dist[x][i];
if (t < t0)
t = (t0 - t + k-1) / k * k + t;
// relax all neighbors
int nt = (t + 1) % k;
if (dist[y][nt] > t+1) {
dist[y][nt] = t+1;
if (!inq[y]) {
q.push(y);
inq[y] = true;
}
}
}
}
}
printf("%d\n", dist[n][0] == 1e9 ? -1 : dist[n][0]);
return 0;
}