Description
给定一张 n n n 点 m m m 边的无向图,求一条 1 → n 1 \to n 1→n 的路径,使得路径上第 k + 1 k+1 k+1 长的边最短。
Analysis
显然可以发现,答案具有单调性。
又因为题目问的是最大值最小,所以,我们选用二分答案来解决问题。
然后问题就变成了:如果答案为
m
i
d
mid
mid,那么
k
k
k 最小是多少?(就是最少要经过几条边权
>
m
i
d
> mid
>mid 的边)
我们可以将边权
>
m
i
d
> mid
>mid 的边的权看做
1
1
1,边权
≤
m
i
d
\le mid
≤mid 的看作
0
0
0,此时求新图上一条
1
→
n
1 \to n
1→n 的最短路即可。
由于新图的边权只有
0
0
0 和
1
1
1,选用 双端队列 bfs 可以很好地解决这个问题。
贴一下这部分的代码:
// Use `0-1 bfs` (a.k.a `deque bfs`).
// s - Start vertex.
// t - Target vertex.
// mid - The pivot value.
auto bfs = [&](int s, int t, int mid) -> bool {
// Reset the arrays needed for the computation.
dis.assign(n, INF);
vis.assign(n, false);
// Enqueue the start vertex.
deque<int> q;
q.push_back(s);
dis[s] = 0;
while (q.size()) {
int u = q.front();
q.pop_front();
if (vis[u]) continue;
vis[u] = true;
for (auto &edge: G[u]) {
int v = edge.first, w = (edge.second > mid);
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
if (w) q.push_back(v); // w(u, v) = 1
else q.push_front(v); // w(u, v) = 0
}
}
}
return dis[t] <= k;
};
Code
// Problem: P1948 [USACO08JAN] Telephone Lines S
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1948
// Memory Limit: 125 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using PII = pair<int, int>;
const int INF = 1e9;
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, m, k;
cin >> n >> m >> k;
vector<vector<PII>> G(n);
for (int i = 0, u, v, w; i < m; i++) {
cin >> u >> v >> w;
u--, v--;
G[u].emplace_back(v, w);
G[v].emplace_back(u, w);
}
vector<int> dis(n, INF);
vector<bool> vis(n, false);
auto bfs = [&](int s, int t, int mid) -> bool {
dis.assign(n, INF);
vis.assign(n, false);
deque<int> q;
q.push_back(s);
dis[s] = 0;
while (q.size()) {
int u = q.front();
q.pop_front();
if (vis[u]) continue;
vis[u] = true;
for (auto &edge: G[u]) {
int v = edge.first, w = (edge.second > mid);
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
if (w) q.push_back(v);
else q.push_front(v);
}
}
}
return dis[t] <= k;
};
int l = 0, r = 1e6;
while (l <= r) {
int mid = l + r >> 1;
if (bfs(0, n - 1, mid)) r = mid - 1;
else l = mid + 1;
}
cout << (r == 1e6? -1: l) << endl;
return 0;
}