c++分层最短路(洛谷飞行路线)acwing版

分层最短路算法是在SPFA算法的基础上,将每个点分成若干层,从而使得每个点之间的转移只在同一层次或上下两个相邻层次之间进行,减少了每轮的迭代次数,优化了算法的效率。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>

using namespace std;

const int MAXN = 10005;
const int MAXM = 100005;
const int INF = 0x3f3f3f3f;

struct Edge {
    int to, nxt, w;
} e[MAXM];

int head[MAXN], tot;
int dis[MAXN], vis[MAXN];

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

void spfa(int s) {
    memset(dis, 0x3f, sizeof(dis));
    dis[s] = 0;
    queue<int> q;
    q.push(s);
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        vis[u] = false;
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (dis[v] > dis[u] + e[i].w) {
                dis[v] = dis[u] + e[i].w;
                if (!vis[v]) {
                    q.push(v);
                    vis[v] = true;
                }
            }
        }
    }
}

int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    tot = 0;
    memset(head, 0, sizeof(head));
    for (int i = 1; i <= m; ++i) {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        add(u, v, w);
    }
    int s, t;
    scanf("%d%d", &s, &t);
    spfa(s);
    printf("%d\n", dis[t]);
    return 0;
}
int layer[MAXN]; //记录每个点所在的层次
int check_layer[MAXN]; //记录每个点是否在队列中

void layer_spfa(int s) {
    memset(dis, 0x3f, sizeof(dis));
    dis[s] = 0;
    layer[s] = 0;
    queue<int> q;
    q.push(s);
    check_layer[s] = true;
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        check_layer[u] = false;
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (layer[u] == layer[v]) {
                if (dis[v] > dis[u] + e[i].w) {
                    dis[v] = dis[u] + e[i].w;
                    if (!check_layer[v]) {
                        q.push(v);
                        check_layer[v] = true;
                    }
                }
            } else if (layer[v] > layer[u]) { //分层
                if (dis[v] > dis[u] + e[i].w) {
                    dis[v] = dis[u] + e[i].w;
                    layer[v] = layer[u] + 1;
                    if (!check_layer[v]) {
                        q.push(v);
                        check_layer[v] = true;
                    }
                }
            }
        }
    }
}

先看题目:

Alice 和 Bob 现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在 n 个城市设有业务,设这些城市分别标记为 0 到 n-1,一共有 m 种航线,每种航线连接两个城市,并且航线有一定的价格。

Alice 和 Bob 现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多 k 种航线上搭乘飞机。那么 Alice 和 Bob 这次出行最少花费多少?

输入格式

第一行三个整数 n,m,k,分别表示城市数,航线数和免费乘坐次数。

接下来一行两个整数 s,t,分别表示他们出行的起点城市编号和终点城市编号。

接下来 mm 行,每行三个整数 a,b,c,表示存在一种航线,能从城市 a 到达城市 b,或从城市 b 到达城市 a,价格为 c。

输出格式

输出一行一个整数,为最少花费。

输入样例:

5 6 1
0 4
0 1 5
1 2 5
2 3 5
3 4 5
2 3 3
0 2 100

输出样例:

8

先给出具体代码:

#include<cstring>
#include<iostream>
#include<queue>

using namespace std;

typedef pair<int, int> PII;

const int N = 2100010, INF = 0x3f3f3f3f;

int n, m, k, s, t;
int dist[N];
int h[N], w[N], e[N], ne[N], idx;
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 u)
{
    memset(dist, INF, sizeof dist);
    dist[u] = 0;
    
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.push({0, u});
    
    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; i = ne[i])
        {
            int j = e[i];
            if(dist[j] > distance + w[i])
            {
                dist[j] = distance + w[i];
                heap.push({dist[j], j});
            }
        }
    }
}

int main()
{
    cin >> n >> m >> k >> s >> t;
    
    memset(h, -1, sizeof h);
    
    while(m --)
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b ,&c);
        add(a, b, c), add(b, a, c);
        for(int j = 1; j <= k; j ++)
        {
            add(j * n + a, j * n + b, c);
            add(j * n + b, j * n + a, c);
            add((j - 1) * n + a, j * n + b, 0);
            add((j - 1) * n + b, j * n + a, 0);
        }
    }
    
    for(int i = 0; i < k; i ++) add(i * n + t, (i + 1) * n + t, 0);
    
    dijkstra(s);
    
    printf("%d\n", dist[n * k + t]);
    
    return 0;
}

1:解释数据:2≤n≤10^4,1≤m≤10^5,0≤k≤10,0≤s, t, a, b<n, a != b, 0≤c≤10^3

本来数据最大值是m,双向边开两倍就可以,但是这里是分层建图,最多有十层,所以要再乘以十

2:初始化h数组

3、加边,下面的是分层建图

建图:从0到k层建k+1张图

           各层之间从上到下建边花费为0

            为防止使用小于k次权力就到达终点,在每层的终点间建花费为0的边连起来

4、dijkstra堆优化版的模板

5、答案输出:到k层的终点为答案

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值