题解 - 旅游巴士

题目描述

小 Z 打算在国庆假期期间搭乘旅游巴士去一处他向往已久的景点旅游。
旅游景点的地图共有 n 处地点,在这些地点之间连有 m 条道路。其中 1 号地点为景区入口,n 号地点为景区出口。我们把一天当中景区开门营业的时间记为 0 时刻,则
从 0 时刻起,每间隔 k 单位时间便有一辆旅游巴士到达景区入口,同时有一辆旅游巴士从景区出口驶离景区。
所有道路均只能单向通行 。对于每条道路,游客步行通过的用时均为恰好 1 单位时间。
小 Z 希望乘坐旅游巴士到达景区入口,并沿着自己选择的任意路径走到景区出口,再乘坐旅游巴士离开,这意味着他到达和离开景区的时间都必须是 k 的非负整数倍 。由
于节假日客流众多,小Z在坐旅游巴士离开景区前只想一直沿着景区道路移动, 而不想在任何地点(包括景区入口和出口) 或者道路上逗留 。
出发前,小 Z 忽然得知:景区采取了限制客流的方法,对于每条道路均设置了一个“开放时间”ai,游客只有不早于ai时刻才能通过这条道路。
请你帮助小 Z 设计一个旅游方案,使得他乘坐旅游巴士离开景区的时间尽量地早。

输入

输入的第一行包含 3 个正整数 n, m, k,表示旅游景点的地点数、道路数,以及旅游巴士的发车间隔。
输入的接下来 m 行,每行包含 3 个非负整数 ui, vi, ai,表示第 i 条道路从地点 ui出发,到达地点 vi,道路的“开放时间”为 ai。

输出

输出一行,仅包含一个整数,表示小 Z 最早乘坐旅游巴士离开景区的时刻。如果不存在符合要求的旅游方案,输出 ‐1。

样例

样例输入

5 5 3
1 2 0
2 5 1
1 3 0
3 4 3
4 5 1

样例输出

6

提示

【样例解释】
请添加图片描述

小 Z 可以在 3 时刻到达景区入口,沿 1 → 3 → 4 → 5 的顺序走到景区出口,并在6 时刻离开。
【数据范围】
对于所有测试数据有:2 ≤ n ≤ 1e4,1 ≤ m ≤ 2 × 1e
4,1 ≤ k ≤ 100,1 ≤ ui, vi ≤ n,0 ≤ ai ≤ 1e6。
请添加图片描述

分析

n为1e4,k为100,可以考虑分层图

由于到达终点的时刻为k的非负整数倍,所以将每个点拆成k个点,dist[i][j]表示到达i号点的总用时 t 满足 t % k == j 的最小花费

分层跑最短路,设当前层数为u,则穿过这条边后层数转移到第 (u + 1) % k 层,取min即可

对于限制ai,由于中途不可以停留,所以我们可以在起点位置延后出发若干个k

然后跑dijkstra即可,时间复杂度为O(nklog(nk))

代码

#pragma GCC optimize(2)
 
#include <bits/stdc++.h>
 
using namespace std;
 
typedef pair<int,int> PII;
 
const int N = 1e4 + 10,M = 2e4 + 10,K = 100 + 10,INF = 0x3f3f3f3f;
 
int n,m,k;
int h[N],e[M],a[M],ne[M],idx;
bool st[N][K];
int dist[N][K];
 
int ceil(int x,int m){
    return (x - 1) / m + 1;
}
 
void add(int A,int B,int C){
    e[idx] = B,a[idx] = C,ne[idx] = h[A],h[A] = idx++;
}
 
int dijkstra(){
    memset(dist,0x3f,sizeof dist);
 
    dist[1][0] = 0;
    priority_queue<PII,vector<PII>,greater<PII>> heap;
    heap.push({0,1});
 
    while(!heap.empty()){
        PII t = heap.top();
        heap.pop();
 
        int ver = t.second,d = t.first;
        if(st[ver][d % k]) continue;
        st[ver][d % k] = true;
 
        for(int i = h[ver];i != -1;i = ne[i]){
            int j = e[i],w = a[i];
 
            int tmp = d;
            if(d < w) tmp += ceil(w - d,k) * k;
 
            if(dist[j][(tmp + 1) % k] > tmp + 1){
                dist[j][(tmp + 1) % k] = tmp + 1;
                heap.push({tmp + 1,j});
            }
        }
 
         
    }
 
    if(dist[n][0] == INF) return -1;
    else return dist[n][0];
}
 
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
 
    memset(h,-1,sizeof h);
 
    cin >> n >> m >> k;
    for(int i = 1,u,v,w;i <= m;i++){
        cin >> u >> v >> w;
        add(u,v,w);
    }
 
    cout << dijkstra();
 
    return 0;
}
  • 13
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值