分层图详解(不那么详细但是好理解的详解AWA)

简介

一般我们看到在最短路中,有k条边的边权可以变化(变成0或是变成w/2基本可以大概确定是分层图的做法了),其实分层图和最短路本质上是一样的,只是在不同层之间连接了一条边,并且根据题目的条件进行转移罢了;

例题

光说理论太枯燥了,直接来看例题:P2939;例题中,可以免费k条边,那么我们可以建立k个图(就是k层图,为什么这样干待会儿会解释):

    cin >> n >> m >> k;
    // STL大法好,我爱STL~!
    // 这里因为点的下标是从1开始,所以要开n+1!
    vector<vector<PII>> g(n + 1);
    while (m--)
    {
        int a, b, w;
        cin >> a >> b >> w;
        // 无向图建图
        g[a].push_back(PII(b, w));
        g[b].push_back(PII(a, w));
    }
    // k层图-->一定要记得把dist全部初始化为INF,不然会出错
    vector<vector<int>> dist(n + 1, vector<int>(k + 1, INF));

那么我们怎么去处理这个分层图呢,其实我们可以发现:每次用掉一个高速公路的机会的时候,可以看成在第k层图和第k-1层图之间建立了一条边,并且这条边的权重为0,那这样就好办了呀,就相当于是把图的点数扩大了k倍的一个图,并且在这个图上面跑一遍Dijkstra就好了呀~(其实就是通过这种方式来实现转移);
于是代码就有了:

// web:https://www.luogu.com.cn/problem/P2939
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
typedef pair<ll, ll> PLL;
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
const ll INFL = 1e18;

int n, m, k;

void dijkstra(vector<vector<PII>>& g, vector<vector<int>>& dist)
{
    dist[1][k] = 0;

	// 这个st存的是每个节点是否确定了最短路(Dijkstra基本知识awa)
    vector<vector<bool>> st(n + 1, vector<bool>(k + 1, false));

	// 这个优先队列的意思是{距离, {点的下标, 在第k层图(或者可以理解成还有几条告诉公路修建的机会)}}
    priority_queue<pair<int, pair<int, int>>, vector<pair<int, pair<int, int>>>, greater<pair<int, pair<int, int>>>>
        pq;
    pq.push(make_pair(0, make_pair(1, k)));

    while (pq.size())
    {
        auto t = pq.top();
        pq.pop();

        int ver = t.second.first;
        int dk = t.second.second;

        if (st[ver][dk])
            continue;
        st[ver][dk] = true;

        for (auto [j, w] : g[ver])
        {
        	// 处理在当前层上的边
            if (dist[j][dk] > dist[ver][dk] + w)
            {
                dist[j][dk] = dist[ver][dk] + w;
                pq.push(make_pair(dist[j][dk], make_pair(j, dk)));
            }
			// 相当于建立了一条边,连这其他层数
            if (dk - 1 >= 0 && dist[j][dk - 1] > dist[ver][dk] + 0)
            {
                dist[j][dk - 1] = dist[ver][dk];
                pq.push(make_pair(dist[j][dk - 1], make_pair(j, dk - 1)));
            }
        }
    }
}

signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n >> m >> k;

    vector<vector<PII>> g(n + 1);
    while (m--)
    {
        int a, b, w;
        cin >> a >> b >> w;
        g[a].push_back(PII(b, w));
        g[b].push_back(PII(a, w));
    }
    vector<vector<int>> dist(n + 1, vector<int>(k + 1, INF));
    dijkstra(g, dist);
    int minn = INF;
    for (int i = 0; i <= k; i++)
        minn = min(minn, dist[n][i]);
    cout << minn << endl;
    return 0;
}

补充(还不理解的建议看看)

如果还有不明白的建议看看我的另一篇博客,这篇博客就是具象化分层图,就只有两层,并且写得更详细;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值