AcWing 853. 有边数限制的最短路

题目: AcWing 853. 有边数限制的最短路

B e l l m a n _ F o r d Bellman\_Ford Bellman_Ford 算法:
  1. memset(dist, 0x3f, sizeof(dist)) 和 dist[1] = 0 源点距离为0,其余点无穷大。
  2. 两层 f o r for for :第一层循环 n − 1 n - 1 n1 次( n n n 指最多经过边的数量,因为 1 1 1 n n n 的最短路最多经过 n − 1 n-1 n1 条边),第二层循环 m m m 次(边的数量),更新最短距离。第二层循环有很多不被更新的点,因为父节点没被更新,其子节点也不会被更新。因此可以优化出 S P F A SPFA SPFA 算法。
  3. 时间复杂度: O ( n m ) O(nm) O(nm)
  4. 与DIjkstra的区别:Dijkstra每次更新的是一个最短距离的点,Bellman_Ford每次更新 i + 1 i+1 i+1 条边以内的点( i i i 是第一层循环变量,初值是0)。
使用场景:
  1. 判断负环:如果 i = n i =n i=n 时还可以进行更新,一定存在负环。因为若没有负环,此时所有的点的距离都被确定了。
  2. 求单源最短路(可以有负权边或边数限制)
代码:
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 510, M = 10010;

// Edge:边的方向及距离。
struct Edge
{
    int a, b, w;
}edges[M]; // 根据边数大小开数组

int dist[N], last[N]; //dist[]:记录距离,last[]:记录上一次的距离
int n, m, k; // n:点数 m:边数 k:最多经过边数

void bellman_ford()
{
	// 初始化距离
    memset(dist, 0x3f, sizeof(dist));
    dist[1] = 0;
    
    // k:更新1号点k条边以内所有点的最短距离。
    // 若循环n - 1次,可算出1到n的最短路。
    for (int i = 0; i < k; i ++ )
    {
        // 这里需要注意使用上一次的dist数组,防止使用了不该更新点
        memcpy(last, dist, sizeof(dist));
        
        for (int j = 0; j < m; j ++ )
        {
            auto e = edges[j];
            // 上一次 a -> b的距离来更新现在的dist[b]
            dist[e.b] = min(dist[e.b], last[e.a] + e.w);
        }
    }
}

int main()
{
    ios::sync_with_stdio(0);cin.tie(0);
    
    cin >> n >> m >> k;
    
    for (int i = 0; i < m; i ++ )
    {
        int a, b, w;
        cin >> a >> b >> w;
        edges[i] = {a, b, w};
    }
    
    bellman_ford();
    
    // 由于存在负权边,但有最多边数限制,dist[n]不会是-INF,但可能比INF要小
    if (dist[n] > 0x3f3f3f3f / 2) cout << "impossible" << endl;
    else cout << dist[n] << endl;
    
    return 0;
}
参考资料:

https://blog.csdn.net/dshf_1/article/details/108144085
https://zhuanlan.zhihu.com/p/72185593
https://www.cnblogs.com/nonames/p/12657562.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值