P3371 单源最短路径 Bellman-Ford算法

看到还没人用Bellman-Ford过,赶紧水一发

lz非常弱,求各位大佬轻喷qwq

洛谷题目传送门:P3371

0.“松弛”操作

如果存在一条边 ( u , v ) (u,v) (u,v)通过中继的方式可以让起点到 v v v的距离缩短,那么就通过中继点缩短这个距离。

举个栗子:

(用数组 d i s [ ] dis[] dis[]来表示起点到每个点的距离,以下同样)

一开始, d i s [ 2 ] = 1000 dis[2]=1000 dis[2]=1000 d i s [ 3 ] = 2 dis[3]=2 dis[3]=2(默认起点为1,以下同样)

通过3中继明显比从1直接到2要短,于是我们把 d i s [ 2 ] dis[2] dis[2]更新为 d i s [ 3 ] + 3 = 5 dis[3]+3=5 dis[3]+3=5(从起点到3再到5)

于是你理解了什么是松弛

1.Bellman-Ford

Bellman-Ford的思想是用每条边进行松弛,每条边松弛 n − 1 n-1 n1次,就一定能求出起点到每个点的距离

(如果你能感性理解你就不用看下面了)

为什么?因为每松弛1轮(我们管用每条边都松弛一次叫一轮),最短路就至少“生长”1个点。

再举个例子:

这是个很美丽的有向图。

最开始,除了 d i s [ 1 ] dis[1] dis[1]之外,其他所有 d i s dis dis都是无穷大。(到不了距离不就是无穷大嘛)。

第1轮松弛:

d i s [ 1 ] = 0 dis[1]=0 dis[1]=0(自己到自己距离肯定是0)

d i s [ 2 ] = m i n ( d i s [ 2 ] , d i s [ 1 ] + 1 ) = m i n ( I N F , 1 ) = 1 dis[2]=min(dis[2],dis[1]+1)=min(INF,1)=1 dis[2]=min(dis[2],dis[1]+1)=min(INF,1)=1

d i s [ 3 ] = m i n ( d i s [ 3 ] , d i s [ 1 ] + 5 ) = m i n ( I N F , 1 ) = 1 dis[3]=min(dis[3],dis[1]+5)=min(INF,1)=1 dis[3]=min(dis[3],dis[1]+5)=min(INF,1)=1

(由于边的顺序问题,松弛出来的结果可能不太一样)

(最坏情况可能只松弛1层)

(现在你明白“生长”是什么意思了吗)

第2轮松弛:

d i s [ 4 ] = m i n ( d i s [ 4 ] , d i s [ 2 ] + 2 ) = m i n ( I N F , 3 ) = 3 dis[4]=min(dis[4],dis[2]+2)=min(INF,3)=3 dis[4]=min(dis[4],dis[2]+2)=min(INF,3)=3

第3轮松弛:

d i s [ 3 ] = m i n ( d i s [ 3 ] , d i s [ 4 ] + 1 ) = m i n ( 5 , 4 ) = 4 dis[3]=min(dis[3],dis[4]+1)=min(5,4)=4 dis[3]=min(dis[3],dis[4]+1)=min(5,4)=4

正好松弛了 n − 1 n-1 n1轮。

因为,一个图最多有 n − 1 n-1 n1层:例如, 1 − > 2 − > 3 − > . . . − > n 1->2->3->...->n 1>2>3>...>n,这个图(最坏情况)就正好要松弛 n − 1 n-1 n1轮。

于是你理解了Bellman-Ford。

Code:

#include <bits/stdc++.h>
using namespace std;
#define MAXN 500005
#define INF 0x7fffffff
struct edge{
    int u,v,w;
    edge(){u=v=w=0;}
};
edge g[MAXN],cnt;
int n,m,s,dis[MAXN];
int main(){
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<=m;i++){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        g[i].u=u;
        g[i].v=v;
        g[i].w=w;//存边
    }
    fill(dis+1,dis+1+n,INF);
    dis[s]=0;
    for(int i=1;i<=n-1;i++){//松弛n-1轮
        for(int j=1;j<=m;j++){
            int u=g[j].u,v=g[j].v,w=g[j].w;
            if(dis[u]==INF){continue;}//如果当前边的起点到不了那就没法松弛
            if(dis[v]>dis[u]+w){
                dis[v]=dis[u]+w;
            }
        }
    }
    for(int i=1;i<=n;i++){
        printf("%d ",dis[i]);
    }
    printf("\n");
    return 0;
}

一交,70pts,所以还要优化。

3.优化

但是,很多情况根本不用松弛 n − 1 n-1 n1轮。

如果松弛到中间一轮松弛不动了(也就是 d i s dis dis不变了),那么以后再怎么松弛也不会变(因为已经求出最优解了),为什么可以思考一下。(其实就是我懒得写了233)

AC Code:

#include <bits/stdc++.h>
using namespace std;
#define MAXN 500005
#define INF 0x7fffffff
struct edge{
   int u,v,w;
   edge(){u=v=w=0;}
};
edge g[MAXN],cnt;
int n,m,s,dis[MAXN];
int main(){
   scanf("%d%d%d",&n,&m,&s);
   for(int i=1;i<=m;i++){
       int u,v,w;
       scanf("%d%d%d",&u,&v,&w);
       g[i].u=u;
       g[i].v=v;
       g[i].w=w;
   }
   fill(dis+1,dis+1+n,INF);
   dis[s]=0;
   for(int i=1;i<=n-1;i++){
       bool flag=true;
       for(int j=1;j<=m;j++){
           int u=g[j].u,v=g[j].v,w=g[j].w;
           if(dis[u]==INF){continue;}
           if(dis[v]>dis[u]+w){
               dis[v]=dis[u]+w;
               flag=false;
           }
       }
       if(flag){
           break;//如果没有松弛过那就没必要再松弛了
       }
   }
   for(int i=1;i<=n;i++){
       printf("%d ",dis[i]);
   }
   printf("\n");
   return 0;
}

4.时间复杂度

最坏情况 O ( n m ) O(nm) O(nm),最好情况 O ( m ) O(m) O(m)(但是只有RP爆表的情况下才会达到),非常离谱。如果不是因为数据水 人品好玄学原因,几乎一定会TLE。

还有一种Bellman-Ford优化,叫SPFA已死。所以,我建议在座的各位dalao先学Bellman-Ford再学SPFA

SPFA的最坏情况 O ( n m ) O(nm) O(nm),最好情况 O ( m ) O(m) O(m),但是除非故意卡(非常容易被卡!),一般都是 O ( m ) O(m) O(m)

SPFA留着以后再讲吧,LZ要去吃饭了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值