• 2019牛客多校第四场J free——分层图&&最短路


    题意

    一张无向图,每条边有权值,可以选择不超过 $k$ 条路使其权值变成0,求 $S$ 到 $T$ 的最短路。(同洛谷 P4568

    分析

    首先,分层图最短路可以有效解决这种带有 「阶段性」的最短路,这是分层图最短路的模板题。

    建立 $0~k $ 层相同的图,每层之间相邻的节点之间也用权值为0的边相连(具体操作见代码)。第 $k$ 层表示已经将 $k$ 条道路置为0。最终把每层的终点连向一个超级汇点。最短路就是从第 $0$ 层源点到超级汇点的最短路。

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn = 1e6 + 10;
    const int maxe = 1e7 + 10;  //仔细算算
    const int INF = 0X3f3f3f3f;
    
    struct Edge
    {
        int to, w, next;
    }edges[maxe];
    int head[maxn], cnt;
    int n, m, S, T, K;
    
    void init()
    {
        memset(head, -1, sizeof(head));
        cnt = 0;
    }
    
    inline void AddEdge(int a, int b, int w)
    {
        int id = ++cnt;
        edges[id].to = b;
        edges[id].w = w;
        edges[id].next = head[a];
        head[a] = id;
    }
    
    struct Node
    {
        int u, d;  //节点的编号与距离
        bool operator < (const Node x) const
        {
            return d > x.d;
        }
    };
    int vis[maxn], dis[maxn];
    
    void Dijsktra(int s)
    {
        priority_queue<Node>q;
        memset(vis, 0, sizeof(vis));
        memset(dis, INF, sizeof(dis));
        dis[s] = 0;
        q.push(Node{s, dis[s]});
        while(!q.empty())
        {
            Node x = q.top();q.pop();
            int u = x.u;
            if(vis[u])  continue;
            vis[u] = true;
            for(int i = head[u]; i != -1;i = edges[i].next)
            {
                int v = edges[i].to;
                int w = edges[i].w;
                if(!vis[v] && dis[u] + w < dis[v])
                {
                    dis[v] = dis[u] + w;
                    q.push(Node{v, dis[v]});
                }
            }
        }
    }
    
    int main()
    {
        init();
        scanf("%d%d%d%d%d", &n, &m, &S, &T, &K);
        for(int i = 0;i < m;i++)
        {
            int a, b, w;
            scanf("%d%d%d", &a, &b, &w);
            AddEdge(a, b, w);
            AddEdge(b, a, w);
            for(int j = 1;j <= K;j++)
            {
                AddEdge(a+(j-1)*n, b+j*n, 0);
                AddEdge(b+(j-1)*n, a+j*n, 0);
                AddEdge(a+j*n, b+j*n, w);
                AddEdge(b+j*n, a+j*n, w);
            }
        }
        for(int i = 0;i < K;i++)  AddEdge(T+i*n, T+(i+1)*n, 0);  //将每层终点连接到超级汇点
        Dijsktra(S);
        printf("%d
    ", dis[T+K*n]);
    
        return 0;
    }

    这题可以用分层图的思想来理解,但是可以用dp来写,只需对Dijsktra作一点修改,对 $dis$ 数组增加一维,$dis[i][j]$ 表示到第 $i$ 个点,用了 $j$ 次免费机会的最短路径长度。

    分层图的方法比较直观,但是占用的内存较多。

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn = 1e3 + 10;
    const int maxe = 2e3 + 10;  //仔细算算
    const int maxk = 1e3 + 10;
    const int INF = 0X3f3f3f3f;
    
    struct Edge
    {
        int to, w, next;
    }edges[maxe];
    int head[maxn], cnt;
    int n, m, S, T, K;
    
    void init()
    {
        memset(head, -1, sizeof(head));
        cnt = 0;
    }
    
    inline void AddEdge(int a, int b, int w)
    {
        int id = ++cnt;
        edges[id].to = b;
        edges[id].w = w;
        edges[id].next = head[a];
        head[a] = id;
    }
    
    struct Node
    {
        int u, d, times;  //节点的编号、距离和已经走0通道的次数
        bool operator < (const Node x) const
        {
            return d > x.d;
        }
    };
    int vis[maxn][maxk], dis[maxn][maxk];
    
    void Dijsktra(int s)
    {
        priority_queue<Node>q;
        memset(vis, 0, sizeof(vis));
        memset(dis, INF, sizeof(dis));
        dis[s][0] = 0;
        q.push(Node{s, dis[s][0], 0});
        while(!q.empty())
        {
            Node x = q.top();q.pop();
            int u = x.u, times = x.times;
            if(vis[u][times])  continue;
            vis[u][times] = true;
            for(int i = head[u]; i != -1;i = edges[i].next)
            {
                int v = edges[i].to, w = edges[i].w;
                if(times < K && dis[u][times] < dis[v][times+1])  //走0通道
                {
                    dis[v][times+1] = dis[u][times];
                    q.push(Node{v, dis[v][times+1], times+1});
                }
                if(dis[u][times] + w < dis[v][times])  //不走0通道
                {
                    dis[v][times] = dis[u][times] + w;
                    q.push(Node{v, dis[v][times], times});
                }
            }
        }
    }
    
    int main()
    {
        init();
        scanf("%d%d%d%d%d", &n, &m, &S, &T, &K);
        for(int i = 0;i < m;i++)
        {
            int a, b, w;
            scanf("%d%d%d", &a, &b, &w);
            AddEdge(a, b, w);
            AddEdge(b, a, w);
        }
        Dijsktra(S);
        int ans = INF;
        for(int i = 0;i <= K;i++)  ans = min(ans, dis[T][i]);
        printf("%d
    ", ans);
    
        return 0;
    }

    参考链接:

    1. https://blog.csdn.net/SSL_ZYC/article/details/94959850

    2. https://www.luogu.org/problemnew/solution/P4568?page=2

  • 相关阅读:
    Project Euler 389 Platonic Dice (概率)
    单纯形(相关题目)
    关于C++中的内存泄露
    莫比乌斯反演与积性函数求和筛法中的一些细节
    清华集训2015-Day 2
    bzoj3456-城市规划
    多项式运算的一些技术
    bzoj2302-Problem c
    bzoj4300-绝世好题
    bzoj4726-Sabota?
  • 原文地址:https://www.cnblogs.com/lfri/p/11260593.html
Copyright © 2020-2023  润新知