P1462 通往奥格瑞玛的道路

题面

解析

题面直接说了最大值最小二分无疑
看到这题的应该都想到了最短路吧,但是一看又要管花费,还要管血量立马慌了,但是其实没有那么复杂

二分歪嘴哦经过城市单次交费最大值\(x\)(不是等会代码的x,这里只是便于书写),通过 Dijkstra算法 得到对于这个\(x\)的最小耗血,\(x\)越大能走的路一定不会变少,耗血自然不会少,等到能活下来\(x\)最小,答案就出来了

代码

alt text

会被hack的代码,\(WA\), \(100pts\)

#include <bits/stdc++.h>

using namespace std;

constexpr int N = 5e4 + 2;

int n, m, b;
int v[N], d[N];
bool f[N];
vector<pair<int, int>> a[N];

inline void read() {
    cin >> n >> m >> b;
    for (int i = 1; i <= n; i++) {
        cin >> v[i];
    }
    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});
    }
}

inline bool check(int mx) {
    for (int k = 1; k <= N; k++) {
        d[k] = (1 << 30);
    }
    memset(f, 0, sizeof f);
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;
    q.push({0, 1});
    d[1] = 0;

    while (!q.empty()) {
        auto t = q.top();
        q.pop();
        int now = t.second;
        if (f[now]) continue;
        f[now] = true;
        for (auto i : a[now]) {
            int x = i.first, y = i.second;
            if (v[x] > mx) continue;
            if (d[x] > d[now] + y) {
                d[x] = d[now] + y;
                q.push({d[x], x});
            }
        }
    }

    int mi = d[n];
    if (b - mi >= 0)
        return true;
    else
        return false;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    read();
    int l = 0, r = 1e9;
    int ans = -1;
    while (l <= r) {
        int mid = (l + r) / 2;
        if (check(mid))
            r = mid - 1, ans = mid;
        else
            l = mid + 1;
    }

    if (ans == -1)
        cout << "AFK";
    else
        cout << ans;
    return 0;
}
关于hack

上面的代码,没有考虑原点是否会超收费!

\(AC\),\(100pts\)

#include <bits/stdc++.h>

using namespace std;

constexpr int N = 5e4 + 2;
#define int long long

int n, m, b;
int v[N], d[N];
bool f[N];
vector<pair<int, int>> a[N];

inline void read() {
    cin >> n >> m >> b;
    for (int i = 1; i <= n; i++) {
        cin >> v[i];
    }
    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});
    }
}

inline bool check(int mx) {
    if (v[1] > mx) return false;
    for (int k = 1; k <= N; k++) {
        d[k] = (1 << 30);
    }
    memset(f, 0, sizeof f);
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;
    q.push({0, 1});
    d[1] = 0;

    while (!q.empty()) {
        auto t = q.top();
        q.pop();
        int now = t.second;
        if (f[now]) continue;
        f[now] = true;
        for (auto i : a[now]) {
            int x = i.first, y = i.second;
            if (v[x] > mx) continue;
            if (d[x] > d[now] + y) {
                d[x] = d[now] + y;
                q.push({d[x], x});
            }
        }
    }

    int mi = d[n];
    if (b - mi >= 0)
        return true;
    else
        return false;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    read();
    int l = 0, r = 1e9;
    int ans = -1;
    while (l <= r) {
        int mid = (l + r) / 2;
        if (check(mid))
            r = mid - 1, ans = mid;
        else
            l = mid + 1;
    }

    if (ans == -1)
        cout << "AFK";
    else
        cout << ans;
    return 0;
}

做题经验总结

  1. “最大值最小 / 最小值最大”
    优先想到二分答案,这是固定套路。

  2. 二分 + 最短路
    先明确限制条件:哪些点、哪些边可以走,不要漏约束。

  3. 起点、终点也是路径的一部分
    必须一起判断是否合法,不要默认起点一定能走。
    漏判这里,几乎必 WA / 被 hack。

  4. Dijkstra 易错三要点

    • 一定要写 q.pop()
    • 距离开 long long 防溢出
    • vis 数组每次 check 都要清空
  5. 无穷大赋值建议
    memsetlong long 容易出错,
    直接循环赋 1e18 最稳妥。

  6. 格式分不能丢
    题目要求输出 AFK 之类的字符串,一定要写对。

  7. 被 hack 多半是漏边界
    常见坑:起点、终点、0 权、重边、自环、不连通。

一句话核心

路径上的点限制,起点终点也算点,必须一起判合法。


看懂?点赞?

posted @ 2026-04-04 11:09  PCMSFV  阅读(7)  评论(0)    收藏  举报