BellmanFord判环算法

转自 http://blog.csdn.net/niushuai666/article/details/6791765

Dijkstra算法是处理单源最短路径的有效算法,但它局限于边的权值非负的情况,若图中出现权值为负的边,Dijkstra算法就会失效,求出的最短路径就可能是错的。

这时候,就需要使用其他的算法来求解最短路径,Bellman-Ford算法就是其中最常用的一个。该算法由美国数学家理查德•贝尔曼(Richard Bellman, 动态规划的提出者)和小莱斯特•福特(Lester Ford)发明。

适用条件&范围:
单源最短路径(从源点s到其它所有顶点v);
有向图&无向图(无向图可以看作(u,v),(v,u)同属于边集E的有向图);
边权可正可负(如有负权回路输出错误提示);
差分约束系统;

Bellman-Ford算法的流程如下:
给定图G(V, E)(其中V、E分别为图G的顶点集与边集),源点s,数组Distant[i]记录从源点s到顶点i的路径长度,初始化数组Distant[n]为INF, Distant[s]为0;

以下操作循环执行至多n-1次,n为顶点数:
对于每一条边e(u, v),如果Distant[u] + w(u, v) < Distant[v],则另Distant[v] = Distant[u]+w(u, v)。w(u, v)为边e(u,v)的权值;
若上述操作没有对Distant进行更新,说明最短路径已经查找完毕,或者部分点不可达,跳出循环。否则执行下次循环;
为了检测图中是否存在负环路,即权值之和小于0的环路。对于每一条边e(u, v),如果存在Distant[u] + w(u, v) < Distant[v]的边,则图中存在负环路,即是说改图无法求出单源最短路径。否则数组Distant[n]中记录的就是源点s到各顶点的最短路径长度。

可知,Bellman-Ford算法寻找单源最短路径的时间复杂度为O(V*E).
Bellman-Ford算法可以大致分为三个部分
第一,初始化所有点。每一个点保存一个值,表示从原点到达这个点的距离,将原点的值设为0,其它的点的值设为无穷大(表示不可达)。
第二,进行循环,循环下标为从1到n-1(n等于图中点的个数)。在循环内部,遍历所有的边,进行松弛计算。
第三,遍历途中所有的边(edge(u,v)),判断是否存在这样情况:
d(v) > d (u) + w(u,v)
则返回false,表示途中存在从源点可达的权为负的回路。

之所以需要第三部分的原因,是因为,如果存在从源点可达的权为负的回路。则 应为无法收敛而导致不能求出最短路径。

#include <cstdio>
#include <cstring>

const int N = 1010;
const int M = 100010;
const int INF = 0x3f3f3f3f;

struct Edge{
    int u, v, dis;
    Edge() {}
    Edge(int u, int v, int dis): u(u), v(v), dis(dis) {}
}E[M];
int d[N], pre[N];
int n, m;

bool Bellman_Ford(int s) {
    for (int i = 1; i <= n; i++)
        d[i] = (i == s ? 0 : INF);

    //松弛
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            if (d[E[j].v] > d[E[j].u] + E[j].dis) {
                d[E[j].v] = d[E[j].u] + E[j].dis;
                pre[E[j].v] = E[j].u;
            }

    for (int i = 1; i <= m; i++)
        if (d[E[i].v] > d[E[i].u] + E[i].dis)  
            return false;
    return true;
}

int main() {

    return 0;
}

另一种写法:

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;

const int MAXNODE = 510;
const int MAXEDGE = 100010;
typedef int Type;
const Type INF = 0x3f3f3f3f;

struct Edge{
    int u, v, next;
    Type dis;
    Edge() {}
    Edge(int u, int v, Type dis, int next): u(u), v(v), dis(dis), next(next) {}
};

struct BellmanFord{
    int n, m;
    Edge edges[MAXEDGE];
    bool inq[MAXNODE];
    Type d[MAXNODE];
    int cnt[MAXNODE], head[MAXNODE];

    void init(int n) {
        this->n = n;
        memset(head, -1, sizeof(head));
        m = 0;
    }

    void AddEdge(int u, int v, Type dis) {
        edges[m] = Edge(u, v, dis, head[u]);
        head[u] = m++;
    }

    bool negativeCycle() {
        queue<int> Q;
        //inq表示是否在队列中,cnt表示该点入队列几次了
        memset(inq, 0, sizeof(inq));
        memset(cnt, 0, sizeof(cnt));
        //初始化d[i]为0的原因是,如果存在负环的话,肯定会有d[i] < 0,初始化为0,可以免去很多的点再加入队列,算是一个剪枝
        for (int i = 0; i < n; i++) {
            d[i] = 0; inq[i] = true; Q.push(i);
        }

        while (!Q.empty()) {
            int u = Q.front(); Q.pop();
            inq[u] = false;
            for (int i = head[u]; ~i; i = edges[i].next) {
                int v = edges[i].v;
                if (d[v] > d[u] + edges[i].dis) {
                    d[v] = d[u] + edges[i].dis;
                    if (!inq[v]) {
                        inq[v] = true;
                        Q.push(v);
                        if (++cnt[v] > n) return true;
                    }
                }
            }
        }
        return false;
    }

    bool positiveCycle() {
        queue<int> Q;
        memset(inq, 0, sizeof(inq));
        memset(cnt, 0, sizeof(cnt));
        for (int i = 0; i < n; i++) {
            d[i] = 0; inq[i] = true; Q.push(i);
        }

        while (!Q.empty()) {
            int u = Q.front(); Q.pop();
            inq[u] = false;
            for (int i = head[u]; ~i; i = edges[i].next) {
                int v = edges[i].v;
                if (d[v] < d[u] + edges[i].dis) {
                    d[v] = d[u] + edges[i].dis;
                    if (!inq[v]) {
                        inq[v] = true;
                        Q.push(v);
                        if (++cnt[v] > n) return true;
                    }
                }
            }
        }
        return false;
    }

}bell;

int main() {
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值