Bellman-Ford算法:AcWing 853. 有边数限制的最短路

Bellman-Ford算法分析:

给定一张有向图,若对于图中的某一条边 (x, y,z)xyz分别表示一条边的起点、终点、权重),有 dist[y] ≤dist[x]+z成立,则称该边满足三角形不等式

若所有边都满足三角形不等式,则dist数组就是所求最短路。

介绍一下基于迭代思想Bellman-Ford 算法。

它的流程如下:

1.扫描所有边(x, y,z),若dist[y] > dist[x]+z,则用dist[x]+z更新dist[y]

2.重复上述步骤,直到没有更新操作发生。

Bellman-Ford算法的时间复杂度为O(nm)。

代码片段:

int n, m;       // n表示点数,m表示边数
int dist[N];        // dist[x]存储1到x的最短路距离

struct Edge     // 边,a表示出点,b表示入点,w表示边的权重
{
    int a, b, w;
}edges[M];

// 求1到n的最短路距离,如果无法从1走到n,则返回-1。
void bellman_ford()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    // 如果第n次迭代仍然会松弛三角不等式,就说明存在一条长度是n+1的最短路径,由抽屉原理,路径中至少存在两个相同的点,说明图中存在负权回路。
    
    for (int i = 0; i < n; i ++ )//最多n-1条边,那为啥不写成"i<n-1"?是因为判断负环至少要循环n层,如果不需要判断负环则写成这样也没问题
    {
        for (int j = 0; j < m; j ++ )//每一轮迭代松弛m条边
        {
            int a = edges[j].a, b = edges[j].b, w = edges[j].w;
             //结合三角不等式可以清楚理解这里
                dist[b] = min(dist[b], dist[a] + w);
        }
    }
    if(dist[n]>inf/2)
        cout<<-1<<endl;
    else 
        cout<<dist[n]<<endl;
}

上面是最朴素的Bellman-Ford算法AcWing 849.Dijkstra求最短路 I可以用上面的模板直接套用,且无需改变数据范围。

通过的代码如下:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define inf 0x3f3f3f3f
const int N = 510,M = 1e5+10;
int dist[N];
int n,m;
int a,b,w;
struct edge
{
    int a,b,w;
}edges[M];

void bellman_ford()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    
    for (int i = 0; i < n; i ++ )
    {
        for (int j = 0; j < m; j ++ )
        {
            int a = edges[j].a, b = edges[j].b, w = edges[j].w;
                dist[b] = min(dist[b], dist[a] + w);
        }
    }
    if(dist[n]>inf/2)
        cout<<-1<<endl;
    else 
        cout<<dist[n]<<endl;
}
int main()
{
    cin>>n>>m;

    for(int i=0;i<m;i++)
    {
        cin>>a>>b>>w;
        edges[i]={a,b,w};
    }
    bellman_ford();
    return 0;
}

上面给的例子是关于有向图的,对于无向图,可以这样做:AcWing 1129. 热浪

下面给出的例题的问法比较特殊(不超过k条边的最短路),因此还需要对模板进行改动。

例题:AcWing 853. 有边数限制的最短路
在这里插入图片描述
在这里插入图片描述
与上面分析的算法基本一致,只不过题目增加了额外的限制:经过边数不超过k条。

代码上较原模板有所改动,具体见代码。

代码:时间复杂度:o(nm)

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define inf 0x3f3f3f3f
const int N = 500+10,M = 10000+10;
int dist[N],backup[N];
int n,m,k;
int a,b,w;
struct edge
{
    int a,b,w;
}edges[M];
//考虑源点到终点的距离正好是-1的情况
void bellman_ford()
{
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    for(int i=0;i<k;i++)//最多经过k条边
    {
        memcpy(backup,dist,sizeof dist);//每次进行新的迭代前将dist备份,防止因节点串联造成的影响
        for(int j=0;j<m;j++)//每一轮迭代松弛m条边
        {
            int A=edges[j].a,B=edges[j].b,W=edges[j].w;
            dist[B]=min(dist[B],backup[A]+W);//backup[A]用的是上一轮迭代更新的A的距离,类似于滚动数组,可以防止因串联而对B更新造成的影响
        }
    }
    if(dist[n]>inf/2)
        cout<<"impossible"<<endl;
    else 
        cout<<dist[n]<<endl;
}
int main()
{
    cin>>n>>m>>k;
    
    for(int i=0;i<m;i++)
    {
        cin>>a>>b>>w;
        edges[i]={a,b,w};
    }
    bellman_ford();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值