通信线路-二分+双端队列BFS
题目
思路
理解一下题意,找到一条路径使得,源点到终点的路径中第\(k + 1\)大的边权最小,也就是下图标注的\((1->2->5)\)的路径花费为\(4\).

求最大值最小显然我们可以想到二分答案,那么如何二分那,我们先设一个\(x\), 对于\(> x\) 的边权我们设置为\(1\), \(\le x\)的边权设置为\(0\), 那么最短路径含义就是最少经过多少条\(> x\)的边可以到达终点\(n\), 如果经过的点数\(\le k\)的话我们返回\(true\), 显然对于更大的\(x\)他是一定满足该特性的,对于更小的\(x\)他可能不满足该特性,所以是满足二段性的。
对于搜到的\(x\),若其\(dist[x] < k\), 那么我们可以继续向左枚举直到\(0\), 作为边界,位置,所以我们一定可以枚举到\(dist[x] == k\)的点,另外我们在设置\(1e6 + 1\)作为右端点当作图不连通。
假设不存在边界的情况下,搜到的结果一定是答案,因为我们假设\(res < k\), 那么一定存在更小的\(ans\)满足条件
Code
#include <iostream>
#include <cstring>
#include <deque>
using i64 = long long;
const int N = 1e3 + 10, M = 2e4 + 10;
/*
问题:
对于任意的1~n的路径,求该路径上第k+1大的边权最小的方案
二分:
check(x) 从1~n的最少经过>x的路径边数不超过k个
边界处理:
如果从1到N不连通, 肯定会一直check(false) 我们让其搜到Max(L_i) + 1
另外答案可能为0, 即不存在经过1~n超过k个的点,我们的ans会搜到0
复杂度
O(m \times log(MAX(L_i)))
*/
std::deque<int> q1;
int h[N], w[M], e[M], ne[M], idx;
int dist[N];
bool st[N];
int n, p, k;
void add(int a, int b, int c) {
ne[idx] = h[a], h[a] = idx, e[idx] = b, w[idx ++] = c;
}
bool check(int x) {
memset(dist, 0x3f, sizeof dist);
memset(st, false, sizeof st);
q1.push_back(1);
dist[1] = 0;
while (q1.size()) {
int t = q1.front(); q1.pop_front();
if (st[t]) continue;
st[t] = true;
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i], v = w[i] > x;
if (dist[j] > dist[t] + v) {
dist[j] = dist[t] + v;
if (!v) {
q1.push_front(j);
} else q1.push_back(j);
}
}
}
// std::cout << dist[n] << "\n";
return dist[n] <= k;
}
int main() {
memset(h, -1, sizeof h);
std::cin >> n >> p >> k;
for (int i = 1; i <= p; i ++) {
int a, b, l;
std::cin >> a >> b >> l;
add(a, b, l);
add(b, a, l);
}
int l = 0, r = 1e6 + 1;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
if (l == 1e6 + 1) {
std::cout << -1;
} else std::cout << l;
}