bellman-ford

bellman-ford算法解析

1.定义以及复杂度比较

bellman-ford,顾名思义,是一个名叫bellman·ford的人发明的,它主要用于求单源最短路,也就是说他和Dijkstra差不多......

但是!它比Dijkstra更简单!!

而且由于bellman-ford的时间复杂度是O(mn)

Djkstra的复杂度是O(n^2)

所以在m<n时,也就是稀疏图时,它是要比Dijkstra要快的!(在都不优化的情况下)

最重要的一点

bellman-ford能判断负环!

 

说了那么多,bellman-ford到底什么思想呢?

 

2.算法解析

我们假设现在有一个城市,有许多的路口,某些路口有道路相连,每个路口有警察值班

现在来了一位游客,他想知道任意一个路口到路口s的距离

 

我们先让一位警察问问与自己相连路口的警察:从你那里能到s吗?有多远?

如果那个警察知道,那么一定是他与s相连,或者之前已经知道了自己与s的距离

如果他也不知道,那就让他再问问与自己相连路口的警察

......

一直这样下去,总有至少一个警察会问到s路口的警察,他便会把答案一层层返回过去,并且加上两个路口的距离

返回结束,所有人都会知道自己与s的距离

 

看一下这样计算的复杂度,首先游客一定要在所有路口都问一遍距离 n次

对于每次询问,警察要问一遍所有的道路,m次

所以复杂度是O(mn)

 

接下来就是代码实现啦

#include<bits/stdc++.h>
using namespace std;
struct edge
{
    int u,v,w;
}e[10005];
int n,m,cnt;
int p[105];
void print(int s,int t)
{
    if(s==t)
    {
        printf("%d",s);
        return ;
    }
    print(s,p[t]);
    printf("%d",t);
}
void bellman()
{
    int s=1;
    int d[105];
    for(int i=1;i<=n;i++)
    {
        d[i]=1000000;
    }
    d[s]=0;
    for(int k=1;k<=n;k++)
    {
        for(int i=0;i<=cnt;i++)
        {
            int x=e[i].u,y=e[i].v;
            if(d[x]>d[y]+e[i].w)
            {
                d[x]=d[y]+e[i].w;
                p[x]=y;
            }
        }
        printf("%d\n",d[n]);
        //print(s,n);
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        if(n==0&&m==0) return 0;
        cnt=0;
        while(m--)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            e[cnt].u=a;
            e[cnt].v=b;
            e[cnt].w=c;
            cnt++;
            e[cnt].u=b;
            e[cnt].v=a;
            e[cnt].w=c;
            cnt++;
        }
        bellman();
    }
    return 0;
}

(可以结合hdu2544看一看)

当然,如果你足够了解bellman-ford的功能,你就会发现,这段代码没有判断负环的功能

不能判断负环的bellman和Dijkstra有什么区别(bushi)

 

那么下来我们来想一想怎么判断负环

我们已经知道bellman在经过n次松弛后,一定能求出每个点到s的最短路径

但是如果存在负环的话,那每一圈的松弛都会让这个点的权值更少一点,因此无法结束

所以,我们只用判断操作次数是否比n大,就可以判断是否有负环了

代码如下(只改了函数部分)

#include<bits/stdc++.h>
using namespace std;
struct edge
{
    int u,v,w;
}e[10005];
int n,m,cnt;
int p[105];
/*void print(int s,int t)
{
    if(s==t)
    {
        printf("%d",s);
        return ;
    }
    print(s,p[t]);
    printf("%d",t);
}*/
void bellman()
{
    int s=1;
    int d[105];
    for(int i=1;i<=n;i++)
    {
        d[i]=1000000;
    }
    d[1]=0;
    int k=0;
    bool flag=true; 
    while(flag)
    {
        k++;
        flag=false;
        if(k>n)
        {
            cout<<"有负圈"; 
            return ;
        }
        for(int i=0;i<=cnt;i++)
        {
            int x=e[i].u,y=e[i].v;
            if(d[x]>d[y]+e[i].w)
            {
                d[x]=d[y]+e[i].w;
                flag=true;
            }
        }
    }
        
        printf("%d\n",d[n]);
        //print(s,n);
    }
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        if(n==0&&m==0) return 0;
        cnt=0;
        while(m--)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            e[cnt].u=a;
            e[cnt].v=b;
            e[cnt].w=c;
            cnt++;
            e[cnt].u=b;
            e[cnt].v=a;
            e[cnt].w=c;
            cnt++;
        }
        bellman();
    }
    return 0;
}

图论,从入门到放弃......

posted @ 2019-12-14 16:07  枣子*1  阅读(344)  评论(0编辑  收藏  举报