进阶指南图论篇

分层图最短路

给定一张图,求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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值