【题型】分层图最短路

本蒟蒻刚刚开通博客,这是我第一篇文文,写得不好请大家见谅

学最短路已经有一段时间了,也在洛谷上刷了挺多的题,机房第一次考试,本蒟蒻 满(垂)怀(头)信(丧)心(气)地接到考题准备AK爆零全场,却死在了T2上

T2:洛谷P1948

 

这个题学长们改了改数据范围,让k无限大(对于我来说),使这个题只能用二分答案来做,但我还是 义(no)无(zuo)反(no)顾(die)地要用分层图做,可惜,代码没能实现(完美爆零)

关于分层图最短路

大概思路:将同一个图复制成多层,从每个图的每个点向下一层连一条边(至于连下一层的哪个点下面再说)

大约是这样的:

 

 

 (图画地不好请见谅)

可以理解成一栋楼,有很多楼梯,我们可以从一层通过楼梯走到下一层

为什么要建分层图

通过洛谷P4568可以理解

实现道路免费(或道路优惠),而且道路的免费(或优惠)还是不定项的

假如有一条边,连接u点和v点,边权为w

可以从上一层图中的编号为u的点向下一层图的编号为v的点连一条边权为0的边(代表免费)

同时在上一层图中的编号为u的点向本层图的编号为v的点连一条边权为w的边(代表不免费)

这样当你走到u点时,就有两个选择:免费或不免费

而建k层图,就代表着有k次免费的优惠

注意:要把每层图(除了最后一层)的终点向下一层图连一条边权为0的边(防止最佳路径不全部用完k次优惠)

伪代码实现(链式前向星):

scanf("%d %d %d", &n, &p, &k);//输入点的个数n,边的个数p,层数k
    for (int i = 1; i <= p; i++){
        scanf("%d %d %d", &x, &y, &z);//输入x点与y点相连,边权为z
        add(x, y, z);
        //add(y, x, z);无向图需添加这一句
        for (int j = 1; j <= k; j++){
            add(x + (j - 1) * n, y + j * n, 0);//使这个点向下层图连条边
            //add(y + (j - 1) * n, x + j * n, 0);
            add(x + j * n, y + j * n, z);//在下层图中构建与上层图相同的边
            //add(y + j * n, x + j * n, z);
        }
    }

我是用链式前向星做的,也可以用四维数组来存(如果你空间够的话)

伪代码实现(四维数组):

 

scanf("%d %d %d", &n, &p, &k);//输入点的个数n,边的个数p,层数k
    for (int i = 1; i <= p; i++){
        scanf("%d %d %d", &x, &y, &z);//输入x点与y点相连,边权为z
        a[x][1][y][1] = z;//x后的数代表第几层的x,y后的数代表第几层的y
        //a[y][1][x][1] = z;无向图需添加这一句
        for (int j = 1; j <= k; j++){
            a[x][j][y][j+1] =  0;//使这个点向下层图连条边
        //a[y][j+1][x][j] = 0;
            a[x][j][y][j] = z;//在下层图中构建与上层图相同的边
            //a[y][j][x][j] = z;
        }
    }

 

当然肯定还有一些用数组来存但不至于用四维的方式,但我还是个蒟蒻,不会......

还是由dalao们来想办法吧~~

洛谷P4568代码实现:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#define LL long long
#define MAXN 1000001
#define M(i, j) make_pair(i, j)
using namespace std;

int head[MAXN];
int n, p, k, js=0, cnt=0, sum=0;
LL dis[MAXN];
bool vis[MAXN];
priority_queue< pair<int,int>, vector< pair<int,int> >, greater< pair<int,int> > > q;

struct edge{
    int v, nxt, b;
    long long w;
}e[5000001 << 1];

void add(int u, int v, int w){
    e[++js].v = v;
    e[js].w = w;
    e[js].nxt = head[u];
    head[u] = js;
}

void dijkstra(int s){//dij堆优化板子 
    int l, k, v, w;
    memset(vis, 0, sizeof(vis));
    memset(dis, 63, sizeof(dis));
    dis[s] = 0;
    q.push(M(0, s));
    while (!q.empty()){
        l = q.top().first;
        k = q.top().second;
        q.pop();
        if (vis[k]) continue;
        vis[k] = 1;
        for (int i = head[k]; i; i = e[i].nxt){
            v = e[i].v;
            w = e[i].w;
            if (dis[v] > l + w){
                dis[v] = l + w;
                if (!vis[v]) q.push(M(dis[v], v));
            }
        }
    }
}

int main(){
    int x, y, z, s, t;
    scanf("%d %d %d", &n, &p, &k);
    for (int i = 1; i <= p; i++){
        scanf("%d %d %d", &x, &y, &z);
        add(x, y, z);
        add(y, x, z);
        for (int j = 1; j <= k; j++){
            add(x + (j - 1) * n, y + j * n, 0);
            add(y + (j - 1) * n, x + j * n, 0);
            add(x + j * n, y + j * n, z);
            add(y + j * n, x + j * n, z);
        }
    }
    for (int i = 1; i <= k; i++) add(t + (i - 1) * n, t + i * n, 0);//这个是上文的注意(看到有的dalao提醒过)
    dijkstra(s);
    printf("%d\n", dis[t + k * n]);
    return 0;
}

提供分层图最短路的其他题目(洛谷里的):

洛谷P2939

洛谷P4822

洛谷P1948

posted @ 2020-10-02 13:54  _Scaley  阅读(178)  评论(3编辑  收藏  举报