bellman-ford算法

本文介绍了Bellman-Ford算法,一种求解单源最短路径问题的方法,尤其适用于含有负权边的情况,并能检测负权环的存在。通过示例代码展示了算法实现过程及其时间复杂度。

bellman-ford算法是用于解决单源最短路径的算法,与Dijkstra不同的是,它可解决存在负权边的的情况。同时,它也可以检测是否存在存在负权值环。

其基本思路如下:

建立一个距离数组dis[],将源点设为0,其余点的距离初始化为无穷大。

将下列操作循环最多n-1次:

对于每条边,start->end,

if(dis[end] > dis[start] + weight)

                dis[end] = dis[start] + weight;

(该操作称为一次"松弛"操作)

循环n-1次或没有任何一条边能够经过松弛操作可以缩短距离。

接下来就是bellman-ford算法最精彩的地方。我们再循环一次,对每条边再进行一次松弛操作,如果还能缩短距离,说明该图中存在负权环,也就不存在最短路径。


对其理解:

由于距离数组一开始除了源点外,距离都设置为无穷大,所以第一次循环时,必然是松弛与起点相连的边。

其基本内涵相当于从起点开始,不断向外扩展。

如果起点到某点i存在一条最短路径的话,就相当于从源点开始,每次都找到从起点到i点最短路径上的一条边,类似与dfs的思想。

由于最短路径最多有n-1条边,所以最多循环n-1次。同理,如果循环了n-1次后,仍能缩短距离,说明图中存在负权环。

以下是有向图的代码,无向图可将边数乘2当做有向图做。

注意,如果无向图中存在一条无向的负边(源点可达),说明是不存在最短路径的。

代码:

#include <iostream>
#define INF 999999999
using namespace std;

class Edge
{
public:
    int s,e;
    int weight;
};
int main()
{
    int n;
    int N,M,W;
    int k;
    cin>>n;
    cin>>N>>M>>W;
    int *dis = new int [N+1];       //距离数组
    Edge *edge = new Edge [2*M+N];  //M条无向边,W条有向表
    bool flag = true;
    while(n--)
    {
        for(int i=1;i<=N;i++)
            dis[i] = INF;
        dis[1] = 0;                 //初始化距离数组
        flag = true;
        k=0;
        for(int i=0;i<M;i++)//将无向边转为两条有向边
        {
            int s,e,weight;
            cin>>s>>e>>weight;
            edge[k].s = s,edge[k].e = e,edge[k++].weight=weight;    //储存两条边
            edge[k].s = e,edge[k].e = s,edge[k++].weight=weight;
        }
        for(int i=0;i<W;i++)//储存有向边
        {
            int s,e,weight;
            cin>>s>>e>>weight;
            edge[k].s = s,edge[k].e = e,edge[k++].weight = -weight;
        }

        for(int i=0;i<N-1;i++)
        {
            flag = true;
            for(int j=0;j<2*M+W;j++)
            {
                if(dis[edge[j].e] > dis[edge[j].s] + edge[j].weight)
                {
                    dis[edge[j].e] = dis[edge[j].s] + edge[j].weight;
                    flag = false;
                }
            }
            if(flag)
                break;
        }

        flag = false;
        for(int j=0;j<2*M+W;j++)
        {
            if(dis[edge[j].e] > dis[edge[j].s] + edge[j].weight)
            {
                dis[edge[j].e] = dis[edge[j].s] + edge[j].weight;
                flag = true;
                break;
            }
        }

        if(flag)
            cout<<"YES"<<endl;
        else
            cout<<"NO"<<endl;
    }
    return 0;
}

可以看出 时间复杂度为V*E 即结点数乘以边数,时间复杂度较大。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值