分层图最短路
给定一张图,求1 ~ n的路线中,去掉k条边后,求剩下的最大边的最小值是多少。
链接:
AcWing340
洛谷P4568
进阶指南上关于这题的做法不是很好,个人感觉建立好分层图后用disjkra比SPFA更好做,也更好理解。
可以这样想象,每一层都是题目给定的图,层与层之间也是有连接的,连接方式也是题给定的一样,但是边权为0——对应着这条边去掉。所以第一层的1到最后一层的n跑一遍最短路就出结果了,不过还要注意一种特殊情况,1 ~ n的边的数量小于k,所以还要额外加一段代码,连接所有层的n节点,这样连接对答案是没有影响的。
#include<bits/stdc++.h>
using namespace std;
// #define int long long
const int N = 1000000 + 5, mod = 1e9 + 7;
struct node {
int val, v;
bool operator < (const node &k) const {
return val > k.val;
}
};
priority_queue<node> q;
int head[N], v[N], edge[N], nex[N];
int d[N];
bitset<N> f;
int tot;
void add(int x, int y, int z)
{
v[++tot] = y;
edge[tot] = z;
nex[tot] = head[x];
head[x] = tot;
}
int dj(int st, int ed)
{
for (int i = 0; i <= N - 1; i++) d[i] = 1e9;
d[st] = 0;
q.push(node{0, st});
while(!q.empty()){
int x = q.top().v;
q.pop();
if (f[x]) continue;
f[x] = 1;
for (int i = head[x]; i; i = nex[i])
{
int y = v[i], val = edge[i];
if (d[y] > max(d[x] ,val)) {
d[y] = max(d[x] ,val);
q.push(node{d[y], y});
}
}
}
if (d[ed] != 1e9) return d[ed];
else return -1;
}
signed main()
{
int n, p, k;
cin >> n >> p >> k;
int s, t;
// cin >> s >> t;
s = 1, t = n;
for (int i = 1; i <= p; i++) {
int x, y, z;
cin >> x >> y >> z;
for (int j = 0; j <= k; j++) {
if (j < k) {
add(x + j * n, y + (j + 1) * n, 0);
add(y + j * n, x + (j + 1) * n, 0);
}
add(x + j * n, y + j * n, z);
add(y + j * n, x + j * n, z);
}
}
for(int i=1;i<=k;++i) {
add(t + (i - 1) * n, t + i * n, 0);
}
cout << dj(s, t + k * n);
return 0;
}
还有一种二分+disjktra的写法,大于mid的边当做1,小于等于的为0,跑一遍最短路,看看d[n]<=k是否成立。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1000 + 5, mod = 1e9 + 7;
struct node {
int val, v;
bool operator < (const node &k) const {
return val > k.val;
}
};
vector<node> v[N];
priority_queue<node> q;
int d[N];
bitset<N> f;
int n, m, k;
bool check(int mid)
{
for (int i = 1; i <= n; i++) {
d[i] = 1e9;
f[i] = 0;
}
d[1] = 0;
q.push(node{0, 1});
while(!q.empty()){
int x = q.top().v;
q.pop();
if (f[x]) continue;
f[x] = 1;
for (auto i : v[x]) {
int y = i.v, val = i.val;
if (val > mid) val = 1;
else val = 0;
if (d[y] > val + d[x]) {
d[y] = val + d[x];
q.push(node{d[y], y});
}
}
}
return d[n] <= k;
}
signed main()
{
cin >> n >> m >> k;
for (int i = 1; i <= m; i++) {
int x, y, z;
cin >> x >> y >> z;
v[x].push_back(node{z, y});
v[y].push_back(node{z, x});
}
int l = 0, r = 1e9;
while(l < r){
int mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid + 1;
}
if (l == 1e9) cout << -1;
else cout << l;
return 0;
}
再推荐一道很类似的题:
通往奥格瑞玛的道路
也是最短路+二分。大致思路都是一致的。二分一个值,跑一遍最短路看看能不能成立。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e4 + 5, mod = 1e9 + 7;
struct node {
int val, v;
bool operator < (const node &k) const {
return val > k.val;
}
};
vector<node> v[N];
priority_queue<node> q;
int d[N], a[N];
bitset<N> f;
int n, m, k;
bool check(int mid)
{
for (int i = 1; i <= n; i++) {
d[i] = 1e18;
f[i] = 0;
}
d[1] = 0;
q.push(node{0, 1});
while(!q.empty()){
int x = q.top().v;
q.pop();
if (f[x]) continue;
f[x] = 1;
for (auto i : v[x]) {
int y = i.v, val = i.val;
if (a[y] > mid) continue;
if (d[y] > val + d[x]) {
d[y] = val + d[x];
q.push(node{d[y], y});
}
}
}
return d[n] <= k;
}
signed main()
{
cin >> n >> m >> k;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= m; i++) {
int x, y, z;
cin >> x >> y >> z;
v[x].push_back(node{z, y});
v[y].push_back(node{z, x});
}
int l = 0, r = 1e9;
while(l < r){
int mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid + 1;
}
if (l == 1e9) cout << "AFK";
else cout << l;
return 0;
}