P4568 [JLOI2011] 飞行路线

题面

解析

对比最基础的最短路,这个加上了可以在有限次数内乘坐任意航班免费的条件

贪心为什么是错的?

你用样例跑一遍基础dij会发现,选了都是5的那条路,但是你肯定发现走带100的那条路更好,100可以免掉

分层图


引用自洛谷用户:SuperJvRuo

仔细发现,上面的克隆了一份到下面,而那些边权为0的边(他没标),就是免费的航班,好理解的,当你用了0次免费机会你就还在上面(1 ~ 4),用了1次免费机会你会在下面(5 ~ 9),于是有几个k你就克隆几份在下面,然后连上就可以,你就向下的连边是单向的,不用担心走回来,用dij跑这个大图就可以了

注意,不要想当然的最下面那个部分的中点,存在前面直接全免的情况

代码

#include <bits/stdc++.h>

using namespace std;

constexpr int N = 1e6 + 2;

int n, m, k, s, t, f[N], d[N];

vector<pair<int, int>> a[N];

inline void dij(int s) {
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;
    memset(d, 127, sizeof d);
    d[s] = 0;
    q.push({0, s});
    while (!q.empty()) {
        auto p = q.top();
        q.pop();
        int now = p.second;
        if (p.first > d[now]) continue;
        if (f[now]) continue;
        f[now] = true;
        for (auto i : a[now]) {
            int x = i.first, y = i.second;
            if (d[x] > d[now] + y) {
                d[x] = d[now] + y;
                q.push({d[x], x});
            }
        }
    }
}

inline void read() {
    cin >> n >> m >> k;
    cin >> s >> t;
    for (int i = 1; i <= m; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        a[u].push_back({v, w});
        a[v].push_back({u, w});
        for (int j = 1; j <= k; j++) {
            a[u + (j - 1) * n].push_back({v + j * n, 0});
            a[v + (j - 1) * n].push_back({u + j * n, 0}); // 别忘了克隆也要连边哦
            a[u + j * n].push_back({v + j * n, w});
            a[v + j * n].push_back({u + j * n, w});
        }
    }
}

signed main() {
    read();
    dij(s);
    int ans = 1e9;
    for (int i = 0; i <= k; i++) {
        ans = min(ans, d[t + i * n]);
    }
    cout << ans << endl;
    return 0;
}

看懂?点赞?

posted @ 2026-04-06 15:21  PCMSFV  阅读(16)  评论(0)    收藏  举报