Bellman_Ford和Spfa两种方法判断负环

Bellman_Ford是通过N-1次循环求出每个点到原点的最短路的,每次循环遍历所有的边,如果能更新就更新。前面说过为什么N-1次就行(若一个点到起点的最短路需要经过N个点,包括他自己,那么第N次循环就能确定他的最短路)。

  最短路一定是不含环的,如果存在负环就根本不存在最短路了。所以这个方法可以用来判断是否有负环,如果循环了N-1次后还能进行松弛操作,说明有负环。Bellman_Ford的复杂度是O(M*N)。


    int bellman(){   //G条边
        int i,j,k;
        memset(d,INF,sizeof(d));
        d[0]=0;
        for(i=0;i<N-1;i++){
            for(j=0;j<G;j++){
                int u=e[j].u,v=e[j].v;
                    if(d[u]!=INF&&d[u]+w[u][v]<d[v]) d[v]=d[u]+w[u][v];
            }
        }
        for(j=0;j<G;j++){
            int u=e[j].u,v=e[j].v;
                if(d[u]!=INF&&d[u]+w[u][v]<d[v]) return 1;
        }
        return 0;
    }

  因为再次循环的时候有些点的d根本没有被更新,再用这个点去更新别的点的话就浪费时间了。对Bellman_Ford改进,Spfa算法是用一个队列,每次取出一个点,更新和他连接的点,如果被更新的点不在队列中就加入队列中,这样就避免了那个问题。因为是对每个点连接的点操作,这就需要链接表了(可以用vector数组)。还需要一个数组inq判断当前这个点是否在队列里。

  如果用spfa判断是否有负环问题,其实原理也是一个点不能被更新N-1次以上,所以再开一个数组记录每个点被加入队列的次数。在网上看说是一个点被加入N次以上就说明有负环,想了半天为什么是N次以上。。。后来发现因为这是有特殊情况的,N=1,这个点被加入一次,但是没负环。。。所以只要判断这个特殊情况,后面条件写成N-1次以上也是对的。。当然为了方便,直接写成N次以上更好。。网上说这个期望时间复杂度O(2*e),这个我就不知道怎么算的了。。。

    int spfa(){
        queue<int> q;
        memset(d,INF,sizeof(d));
        memset(inq,0,sizeof(inq));
        memset(cnt,0,sizeof(cnt));
        d[0]=0;
        inq[0]=1;
        q.push(0);
        while(!q.empty()){
            int x=q.front();
            q.pop();
            inq[x]=0;
            cnt[x]++;
            if(cnt[x]>N) return 1;
            if(d[x]==INF) continue;
            for(int i=0;i<v[x].size();i++){
                int y=v[x][i].y;
                if(d[x]+v[x][i].t<d[y]){
                    d[y]=d[x]+v[x][i].t;
                    inq[y]=1;
                    q.push(y);
                }
            }
        }
        return 0;
    }

 

    原文作者:Bellman - ford算法
    原文地址: https://blog.csdn.net/qiang_____0712/article/details/84639514
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞