zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    数据绑定表达式语法(Eval,Bind区别)
    使用博客园的第一件事 自定义主题
    sql2000 跨服务器复制表数据
    使用UpdatePanel 局部刷新出现中文乱码的解决方法!!
    MMC不能打开文件MSC文件
    sql 日期 、时间相关
    loaded AS2 swf call function in AS3 holder
    Rewrite the master page form action attribute in asp.net 2.0
    100万个不重复的8位的随机数
    flash 中实现斜切变型
  • 原文地址:https://www.cnblogs.com/lfri/p/11260593.html
Copyright © 2011-2022 走看看