分层图最短路

分层图最短路

概念与使用场景
  • 分层图:顾名思义就是分很多层的图
  • 分层图最短路是指在可以进行分层图的图上解决最短路问题。
  • 常用于题目对边的权值提供可选择的操作
  • 一般模型:在图上,有k次机会可以直接通过一条边,问起点与终点之间的最短路径。
算法思路
  • 将最原始的图作为初始图(第零层),根据所给操作将图分层
  • 每进行k次操作,那么就将图复制成 k份,第 i 层图代表进行了 i 次操作后的图。
分层图构建思路
  • 先将图复制成 k+1 份0~k+1
  • 对于图中的每一条边 <u,v> ui vi+1 建立与题目所给操作相对应的边(i=0,1,…,k)
  • k代表了进行操作的次数,而每层之间点的关系代表了进行了何种操作。
基本模板 O(k*(m+n)log(n))
for(int i = 0; i < m; i ++)
    {
        int u, v, w;
        cin >> u >> v >> w;
        add(u, v, w), add(v, u, w);//建原图

    	//建k次操作后的k层图
        for(int j = 1; j <= k; j ++)
        {
            //建本层,双向
            add(u + j * n, v + j * n, w);
            add(v + j * n, u + j * n, w);

            //连接各层,双向
            add(u + (j - 1) * n, v + j * n, 0);
            add(v + (j - 1) * n, u + j * n, 0);
        }
    }
练习题
洛谷P4568–飞行路线
  • 分析

    • 基础的套用板子题,建好分层图跑最短路即可
    • 要注意分析数据范围,分层图要进一步去考虑层数
  • code

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 2e5 + 10, M = 3e6 + 10;
    const int inf = 0x3f3f3f3f;
    typedef pair<int, int>PII;
    
    int h[N], e[M], ne[M], w[M], idx;
    int n, m, t, k, s;
    int dist[M];
    int st[N];
    
    void add(int a, int b, int c)
    {
        e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
    }
    
    void dijkstra(int sa)
    {
        memset(dist, 0x3f, sizeof dist);
        dist[sa] = 0;
        priority_queue<PII, vector<PII>, greater<PII>> heap;
        heap.push({0, sa});
    
        while (heap.size())
        {
            auto t = heap.top();
            heap.pop();
    
            int ver = t.second, distance = t.first;
    
            if (st[ver]) continue;
            st[ver] = true;
    
            for (int i = h[ver]; i != -1; i = ne[i])
            {
                int j = e[i];
                if (dist[j] > dist[ver] + w[i])
                {
                    dist[j] = dist[ver] + w[i];
                    heap.push({dist[j], j});
                }
            }
        }
    }
    
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0), cout.tie(0);
    
        cin >> n >> m;
        cin >> s >> t;
        memset(h, -1, sizeof h);
    
        for(int i = 0; i < m; i ++)
        {
            int u, v, w, last;
            //通过记录上1个点来完成建边
            cin >> u >> v >> w;
    
            for(int j = 1; j <= w; j ++)
            {
                int x;
                cin >> x;
    
                add(i * n + x, m * n + x, 0);
                add(n * m + x, i * n + x, u);
                if(j != 1)
                {
                    add(i * n + x, i * n + last, v);
                    add(i * n + last, i * n + x, v);
                }
                last = x;
            }
        }
    
        dijkstra(s + n * m);
    
       if(dist[n * m + t] == inf)
            cout << -1 << endl;
        else
            cout << dist[n * m + t] << "\n";
    
        return 0;
    }
    
洛谷P2939–RTG
  • 分析

    • 读清题意后可发掘一上一题高度相似
  • code

    #include<bits/stdc++.h>
    
    using namespace std;
    typedef pair<int, int> PII;
    
    const int N = 2e6 + 10, M = 5e6 + 10;
    const int inf = 0x3f3f3f3f;
    
    int n, m, k, t;
    int h[N], e[M], ne[M], w[M], idx;
    int dist[N];
    bool st[N];
    
    void add(int a, int b, int c)
    {
        e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
    }
    
    void dijkstra(int sa)
    {
        memset(dist, 0x3f, sizeof dist);
        dist[sa] = 0;
        priority_queue<PII, vector<PII>, greater<PII>> heap;
        heap.push({0, sa});
    
        while (heap.size())
        {
            auto t = heap.top();
            heap.pop();
    
            int ver = t.second, distance = t.first;
    
            if (st[ver]) continue;
            st[ver] = true;
    
            for (int i = h[ver]; i != -1; i = ne[i])
            {
                int j = e[i];
                if (dist[j] > dist[ver] + w[i])
                {
                    dist[j] = dist[ver] + w[i];
                    heap.push({dist[j], j});
                }
            }
        }
    }
    
    
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0), cout.tie(0);
    
        memset(h, -1, sizeof h);
    
        cin >> n >> m >> k;
        for(int i = 0; i < m; i ++)
        {
            int u, v, w;
            cin >> u >> v >> w;
            add(u, v, w), add(v, u, w);
    
            for(int j = 1; j <= k; j ++)
            {
                add(u + j * n, v + j * n, w);
                add(v + j * n, u + j * n, w);
    
                add(u + (j - 1) * n, v + j * n, 0);
                add(v + (j - 1) * n, u + j * n, 0);
            }
        }
    
        dijkstra(1);
    
        int ans = inf;
        for(int i = 0; i <= k; i ++)
        {
            ans = min(ans, dist[n + i * n]);
        }
        cout << ans << "\n";
    
        return 0;
    }
    
牛客–小雨坐地铁
  • 分析

    • 此题在建图上加了难度,没有所谓覆盖所有点初始状态的初始图
    • 我们可以创造一个虚图,来建立操作之间的联系
  • code

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 2e5 + 10, M = 3e6 + 10;
    const int inf = 0x3f3f3f3f;
    typedef pair<int, int>PII;
    
    int h[N], e[M], ne[M], w[M], idx;
    int n, m, t, k, s;
    int dist[M];
    int st[N];
    
    void add(int a, int b, int c)
    {
        e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
    }
    
    void dijkstra(int sa)
    {
        memset(dist, 0x3f, sizeof dist);
        dist[sa] = 0;
        priority_queue<PII, vector<PII>, greater<PII>> heap;
        heap.push({0, sa});
    
        while (heap.size())
        {
            auto t = heap.top();
            heap.pop();
    
            int ver = t.second, distance = t.first;
    
            if (st[ver]) continue;
            st[ver] = true;
    
            for (int i = h[ver]; i != -1; i = ne[i])
            {
                int j = e[i];
                if (dist[j] > dist[ver] + w[i])
                {
                    dist[j] = dist[ver] + w[i];
                    heap.push({dist[j], j});
                }
            }
        }
    }
    
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0), cout.tie(0);
    
        cin >> n >> m;
        cin >> s >> t;
        memset(h, -1, sizeof h);
    
        for(int i = 0; i < m; i ++)
        {
            int u, v, w, last;
            //通过记录上1个点来完成建边
            cin >> u >> v >> w;
    
            for(int j = 1; j <= w; j ++)
            {
                int x;
                cin >> x;
    
                //向“虚图”创建联系
                add(i * n + x, m * n + x, 0);
                add(n * m + x, i * n + x, u);
                if(j != 1)
                {
                    add(i * n + x, i * n + last, v);
                    add(i * n + last, i * n + x, v);
                }
                last = x;
            }
        }
    
        dijkstra(s + n * m);
    
       if(dist[n * m + t] == inf)
            cout << -1 << endl;
        else
            cout << dist[n * m + t] << "\n";
    
        return 0;
    }
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值