bellman-ford (有边数限制的最短路,可能存在负权路)

在这里插入图片描述

最短路径中不能包含负权回路有负权回路的边可能不存在最短路。因为每次经过负权回路,路径的权值会减少。有些图结构中会存在负权边,用于表达通过某条途径可以降低总消耗,在有向图中,负权边不一定会形成负权回路,所以在一些计算最短路径算法中,负权边也可以计算出最短路径;在无向图中,负权边就意味着负权回路,所以无向图中不能存在负权边
作者:zhipingChen
链接:https://www.jianshu.com/p/b876fe9b2338
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

松弛函数

对边集合 E E E 中任意边,以 w ( u , v ) w(u,v) w(u,v)表示顶点 u u u 出发到顶点 v v v 的边的权值,以 d [ v ] d[v] d[v] 表示当前从起点 s s s 到顶点 v v v 的路径权值
若存在边 w ( u , v ) w(u,v) w(u,v),使得: d [ v ] > d [ u ] + w ( u , v ) d[v]>d[u]+w(u,v) d[v]>d[u]+w(u,v)
则更新 d [ v ] d[v] d[v] 值: d [ v ] = d [ u ] + w ( u , v ) d[v]=d[u]+w(u,v) d[v]=d[u]+w(u,v)
所以松弛函数的作用,就是判断是否经过某个顶点,或者说经过某条边,可以缩短起点到终点的路径权值
为什么将缩短距离的操作称之为“松弛”,不妨理解为,选择某种方式后,到达目的的总代价降低了。什么名字无关紧要,不必纠结。

作者:zhipingChen
链接:https://www.jianshu.com/p/b876fe9b2338
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

B e l l m a n _ f o r d Bellman\_ford Bellman_ford算法

可以用来求负权边最短路
找一个图是否有负环,但一般不用 B e l l m a n _ f o r d Bellman\_ford Bellman_ford来做,一般用 s p f a spfa spfa来做
有边数限制的只能用 B e l l m a n _ f o r d Bellman\_ford Bellman_ford来做,其他都不行

模板题:AcWing 853.有边数限制的最短路
步骤如下:
1、老规矩,初始化距离数组:
m e m s e t ( d i s t , 0 x 3 f , s i z e o f ( d i s t ) ) memset(dist, 0x3f, sizeof(dist)) memset(dist,0x3f,sizeof(dist))
d i s t [ 1 ] = 0 ; dist[1]=0; dist[1]=0;

2、 f o r ( i = 1 ; i < = k ; + + i ) for(i=1;i<=k;++i) for(i=1;i<=k;++i) 这个含义是迭代k次,就能求出不超过 k k k 条边的最短距离

3、 f o r 所 有 边 a , b , w for 所有边 a,b,w fora,b,w,如果两个点之间不存在边就不用更新,我们只更新存在两个点之间存在边的边
d i s t [ b ] = m i n ( d i s t [ b ] , d i s t [ a ] + w ) dist[b]=min(dist[b],dist[a]+w) dist[b]=min(dist[b],dist[a]+w)PS:这一步称为松弛操作

4、 i f ( d i s t [ n ] > 0 x 3 f 3 f 3 f 3 f / 2 ) if(dist[n]>0x3f3f3f3f/2) if(dist[n]>0x3f3f3f3f/2),说明不超过 k k k 条边,到不了 n n n 这个点
理由:假如 m m m n n n 存在一个负权 − 2 -2 2,那么更新的时候 d i s t [ n ] = m i n ( d i s t [ n ] , d i s t [ m ] + ( − 2 ) ) dist[n]=min(dist[n],dist[m]+(-2)) dist[n]=min(dist[n],dist[m]+(2))
这个时候的 d i s t [ n ] dist[n] dist[n] 就不是 0 x 3 f 3 f 3 f 3 f 0x3f3f3f3f 0x3f3f3f3f 了,而是比 0 x 3 f 3 f 3 f 3 f 0x3f3f3f3f 0x3f3f3f3f还小的数,但不会小太多,这道题最多小 10000 ∗ 500 = 5000000 10000*500=5000000 10000500=5000000,五百万这么多。所以除以 2 2 2 就是了

注意在更新距离的时候我们一定要备份一个距离数组,防止出现串联用多次
这道题我们用结构体来存边,AC代码如下:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+10;

struct Edge
{
    int a,b,c;
}edge[MAXN];
int dist[MAXN],backup[MAXN],n,m,k,x,y,z;

int bellman_ford()
{
    memset(dist,0x3f,sizeof(dist));//初始化
    dist[1]=0;
    
    for(int i=1;i<=k;++i)
    {
        memcpy(backup,dist,sizeof(dist));//记得备份啊
        for(int j=1;j<=m;++j)
            dist[edge[j].b]=min(dist[edge[j].b],backup[edge[j].a]+edge[j].c);
    }
    
    if(dist[n]>0x3f3f3f3f/2)
        printf("impossible");
    else
        printf("%d",dist[n]);
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d%d",&x,&y,&z);
        edge[i]={x,y,z};
    }
    
    bellman_ford();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值