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\n", 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\n", ans);

    return 0;
}

 

 

参考链接:

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

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

上一篇:POJ-2195(最小费用最大流+MCMF算法)


下一篇:繁忙的都市(kruskal)