三个简单最短路

题目一:E - Train

题目链接:E - Train

给定 N 个编号为 1 至 N 的城市以及 M 条铁路。

第 i 条铁路连接城市 Ai​ 和 Bi​,每当时间为 Ki​ 的倍数时会同时、分别从 Ai​ 和 Bi​ 发出开往对方的列车,列车从出发至到达花费 Ti​ 时间。

开始时你在城市 X,输出你到达城市 Y 的最早时间。若无法到达,输出 -1

忽略转车所需要的时间。即,当你 T 时刻到达某个城市时,可以立刻乘坐 T 时刻从这个城市发出的列车。

数据输入范围:

2 ≤ N ≤ 105      0 ≤ M ≤ 105

1 ≤ X,Y ≤ N      X != Y      1 ≤ Ai​,Bi​ ≤ N

Ai​  != Bi​    1 ≤ Ti​ ≤ 109  1 ≤ Ki​ ≤ 109

单源最短路模板

1. 建图时,要将每一条边的花费时间,以及出发时间的倍数都保存下来,用于后面跑最短路。

2. 计算发车的时间 

if(time % k == 0) return time;

return time + k - time % k;   查看time     是否是ki的倍数, 不是就将time变成最小的ki倍数值。

3. Dijkstra算法。

#include 
using namespace std;

using i64 = long long;
using i128 = __int128;
using ui64 = unsigned long long;

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int n, m, x, y;
    cin >> n >> m >> x >> y;

    vector>> g(n+1);
    
    for(int i=1; i<=m; i++) {
        int u, v, t, k;
        cin >> u >> v >> t >> k;
        g[u].push_back({v, t, k});
        g[v].push_back({u, t, k});
    }

    priority_queue> q;
    vector dis(n+1, LONG_LONG_MAX); // 开long long

    auto go = [&](i64 time, int k)->i64{
        if(time % k == 0) return time;
        return time + k - time % k;
    }; // 计算发车时间

    dis[x] = 0;
    q.push({0LL, x});
    vectorvis(n+1);
    while(!q.empty()) {
        auto [time, u] = q.top();
        q.pop();
        if(vis[u])continue;
        vis[u] = 1;
        for( auto[v, t, k] : g[u]) {
            i64 cur  = go(-time, k);
            if(dis[v] > cur + t) {
                dis[v] = cur + t;
                q.push({-dis[v], v}); // 大顶堆采用负数入堆
            }
        }
    }
    
    cout << (dis[y]==LONG_LONG_MAX? -1: dis[y]) << "\n";
    return 0;
}

题目二:D - Wall

题目链接:D - Wall

题目大意:

你面前有一堵墙,墙上有数字,你需要将墙上的数字都变成 1 。
现在给出一个 W×H 的矩阵 A 表示墙上数字的情况。
其中若 Ai,j​=−1 ,则表示位置 (i,j) 上没有数字,否则 Ai,j​ 的值表示墙上 (i,j) 位置的数字。
你还有一张 10×10 的表 C,其中 Ci,j​ 表示把数字 i 转化成数字 j 所需要的花费。求花费的最小值

具体题目见原题链接;

算法:Floyd 算法           模板

1. 先将矩阵C使用Floyd算法,跑出每一个的 数字i 到 j 的最短路。

2. 输入矩阵A时, 对于 1 和 -1 ,不用计算花费, 其他的 ans += dis[x][1].

#include 
using namespace std;

using i64 = long long;
using i128 = __int128;
using ui64 = unsigned long long;

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int n, m;
    cin >> n >> m;
    vector> dis(10,vector(10));

    for(int i=0; i<=9; i++) {
        for(int j=0; j<=9; j++) {
            cin >> dis[i][j];
        }
    }
    // 最短路
    for(int k=0; k<=9; k++) {
        for(int i=0; i<=9; i++) {
            for(int j=0; j<=9; j++) {
                if(dis[i][k] + dis[k][j] < dis[i][j])
                    dis[i][j] = dis[i][k] + dis[k][j];
            }
        }
    }

    int ans = 0;
    for(int i=0; i> x;
            if(abs(x)==1)continue; // 1, -1 不用
            ans += dis[x][1];
        }
    }

    cout << ans << "\n";
    return 0;
}

题目三:E - Travel by Car

题目链接:E - Travel by Car

题目大意:

n 个小镇,m 条双向道路。第 i 条道路从 ai​ 通向 bi​,长度为 ci​。车的油箱容量为 L,当行驶到一个镇上时可以选择加满油或者什么都不做。行驶单位长度的距离消耗一单位的油。

现在回答 Q 个问题:

油箱现在为满,从 si​ 到 ti​,最少需要加油多少次,或者输出-1。

2≤n≤300,0≤m≤n(n−1)/2,无重边,无自环。1≤ci​,L≤1e9。

算法:Floyd算法                            模板

1. 看数据量 Floyd 算法支持,可以使用。

2.做法, 先将可以用一次L升油的路给计算出来, 在这n个点上跑距离的最短路Floyd  也就是 dis矩阵

3.将两点之间小于 等于L 的距离要花费一桶油更新为1  ,  dis1矩阵

3.最后在 油上的花费再一次的跑一边 Floyd 

4. 预处理答案, 如果油数为INT_MAX/2 则表示无法到达,记为-1, 否则减一(因为在开始车的状况为满油)

#include 
using namespace std;

using i64 = long long;
using i128 = __int128;
using ui64 = unsigned long long;

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int n, m, L;
    cin >> n >> m >> L;
    vector> dis(n+1,vector(n+1, LONG_LONG_MAX/2));
    vector> dis1(n+1, vector(n+1, INT_MAX/2));
    // 建边
    for(int i=0; i> u >> v >> w;
        dis[u][v] = w;
        dis[v][u] = w;
    }
    // 先算点到点的距离最短路
    for(int k=1; k<=n; k++) {
        for(int i=1; i<=n; i++) {
            for(int j=1; j<=n; j++) {
                if(dis[i][j] > dis[i][k] + dis[k][j])dis[i][j] = dis[i][k] + dis[k][j];
            }
        }
    }
    // 统计那些点到点 可以只花费一次的 L 升油
    for(int i=1; i<=n; i++) {
        for(int j=1; j<=n; j++) {
            if(dis[i][j]<=L)
                dis1[i][j]=1;
        }
    }
    // 最后在到花费的油上跑最短路
    for(int k=1; k<=n; k++) {
        for(int i=1; i<=n; i++) {
            for(int j=1; j<=n; j++) {
                if(dis1[i][j] > dis1[i][k] + dis1[k][j])dis1[i][j] = dis1[i][k] + dis1[k][j];
            }
        }
    }
    // 预处理答案
    for(int i=1; i<=n; i++) {
        for(int j=1; j<=n; j++) {
            if(dis1[i][j]==INT_MAX/2)
                dis1[i][j]=-1;
            else dis1[i][j]--;
        }
    }

    int q;
    cin >> q;
    while(q--) {
        int u, v;
        cin >> u >> v;
        cout << dis1[u][v] << "\n";
    }
    return 0;
}

感谢收看与点赞, 欢迎大佬指正。

你可能感兴趣的:(算法,最短路,Dijkstra,Floyd)