P1462 通往奥格瑞玛的道路 题解
解析
题面直接说了最大值最小,二分无疑
看到这题的应该都想到了最短路吧,但是一看又要管花费,还要管血量立马慌了,但是其实没有那么复杂
二分歪嘴哦经过城市单次交费最大值\(x\)(不是等会代码的x,这里只是便于书写),通过 Dijkstra算法 得到对于这个\(x\)的最小耗血,\(x\)越大能走的路一定不会变少,耗血自然不会少,等到能活下来且\(x\)最小,答案就出来了
代码

会被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;
}
做题经验总结
-
“最大值最小 / 最小值最大”
优先想到二分答案,这是固定套路。 -
二分 + 最短路
先明确限制条件:哪些点、哪些边可以走,不要漏约束。 -
起点、终点也是路径的一部分
必须一起判断是否合法,不要默认起点一定能走。
漏判这里,几乎必 WA / 被 hack。 -
Dijkstra 易错三要点
- 一定要写
q.pop() - 距离开
long long防溢出 vis数组每次check都要清空
- 一定要写
-
无穷大赋值建议
memset对long long容易出错,
直接循环赋1e18最稳妥。 -
格式分不能丢
题目要求输出AFK之类的字符串,一定要写对。 -
被 hack 多半是漏边界
常见坑:起点、终点、0 权、重边、自环、不连通。
一句话核心
路径上的点限制,起点终点也算点,必须一起判合法。
看懂?点赞?

浙公网安备 33010602011771号