公路 (CSP-J 2023)

公路 (CSP-J 2023)

多层图DP松弛,最短路变形。

题意如下:给定一个有向图,边有开放时间,入口11和出口nnKK时间开放一次,每条边通过时间为11,问从入口到出口的最短时间。根据条件,路线需要满足两个条件:

  1. 从入口到出口经过KK的整数倍条边
  2. 经过每条边时的起点时间大于等于这条边的开放时间aia_i

点数N<104N \lt 10^4, 边数M<2104M \lt 2\cdot 10^4, 时间间隔K<100K \lt 100

我们看到KK很小,所以从KK入手。把时间模KK,这样我们就可以把时间转换为模KK的余数,图变成了KK层图。

我们定义dist[i][j]\text{dist}[i][j]为从入口经过modK\mod Kjj条边,到达点ii的最早时间。然后答案就是dist[n][0]\text{dist}[n][0]

然后用一个队列来管理活跃的点(开始为11,起点),不断松弛dist[i][j]\text{dist}[i][j]数组就可以了。

具体就是,对于队列中的下一个点xx,我们遍历它的所有邻居yy,然后更新dist[y][]\text{dist}[y][\cdot],我们对i=0..K1i=0..K-1

  1. 如果dist[x][i]\text{dist}[x][i]不早于xyx-y边的开放时间,则t=dist[x][i]t=\text{dist}[x][i],如果早于,则t是大于等于开放时间的第一个modK\mod K同余数。
  2. 如果t+1t+1小于dist[y][(t+1)modK]\text{dist}[y][(t+1)\mod K],则更新dist[y][(t+1)modK]\text{dist}[y][(t+1)\mod K]t+1t+1,并把yy加入队列。

细节见程序。

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