Bellman-Ford算法

Bellman-Ford可以求有负权的图的最短路,也可以判断是否有负环存在。

单源最短路模板

//从s出发到所有点的最短距离
void BellmanFord(int s) {
    for (int i = 1; i <= n; ++i) d[i] = INF;
    d[s] = 0;
    while (true) {
        bool update = false;
        for (int i = 0; i < E; ++i) {
            edge e = es[i];
            if (d[e.from] != INF && d[e.to] > d[e.from] + e.cost) {
                d[e.to] = d[e.from] + e.cost;
                update = true;
            }
        }
        if (!update) break;
    }
}

如果没有环,最多更新n-1次,n是点的个数,如果更新了n次,证明图中有环。因为求的是最短路,所以一般判断的是负环,正环也可以判断的。

poj3259 Wormholes

求是否存在负环

#include <iostream>
#include <vector>
#include <cstdio>
using namespace std;
const int INF = 0x5f5f5f5f;
const int MAX_V = 505;
const int MAX_E = 2705;

struct edge { int from, to, cost; };

vector<edge> G;

int d[MAX_V];
int V, E, W;

bool BellmanFord(int n)
{
    for (int i = 1; i <= n; ++i) d[i] = INF;
    d[1] = 0;
    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j < G.size(); ++j) {
            int u = G[j].from, v = G[j].to;
            if (d[u] != INF && d[v] > d[u]+G[j].cost) {
                d[v] = min(d[v], d[u]+G[j].cost);
                if (i == n) return true;
            }
        }
    }
    return false;
}

int main()
{
//freopen("in.txt", "r", stdin);
    int t;
    scanf("%d", &t);
    while (t--) {
        G.clear();
        scanf("%d%d%d", &V, &E, &W);
        int u, v, w;
        for (int i = 0; i < E; ++ i)
        {
            scanf("%d%d%d", &u, &v, &w);
            G.push_back(edge{u, v, w});
            G.push_back(edge{v, u, w});
        }
        for (int i = 0; i < W; ++ i)
        {
            scanf("%d%d%d", &u, &v, &w);
            G.push_back(edge{u, v, -w});
        }
        if (BellmanFord(V)) puts("YES");
        else puts("NO");
    }
    return 0;
}

poj1860 Currency Exchange

求是否存在正环

//poj1860
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int N = 105;
const int INF = 0x5f5f5f5f;
struct Edge {
    int from, to;
    double rate, com;
};
vector<Edge> G;
double d[N];

bool BellmanFord(int s, double money, int n) {
    for (int i = 1; i <= n; ++i) d[i] = 0;
    d[s] = money;
    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j < G.size(); ++j) {
            int u = G[j].from, v = G[j].to;
            if (d[v] < (d[u]-G[j].com)*G[j].rate) {
                d[v] = (d[u]-G[j].com)*G[j].rate;
                if (i == n) return true;
            }
        }
    }
    return false;
}

int main()
{
//freopen("in.txt", "r", stdin);
    int n, m;
    int s;
    double money;
    while (~scanf("%d%d%d%lf", &n, &m, &s, &money)) {
        G.clear();
        while (m--) {
            int u, v;
            double r1, c1, r2, c2;
            scanf("%d%d%lf%lf%lf%lf", &u, &v, &r1, &c1, &r2, &c2);
            G.push_back((Edge){u, v, r1, c1});
            G.push_back((Edge){v, u, r2, c2});
        }
        if (BellmanFord(s, money, n)) puts("YES");
        else puts("NO");
    }
    return 0;
}

Bellman-Ford的复杂度很高,可以用队列来优化(也就变成了传说中的spfa)

模板:

const int INF = 0x5f5f5f5f;
const int N = 105;
struct Edge {
    int to, cost;
};
vector<Edge> G[N];
int cnt[N], pre[N], d[N];
bool inq[N];
bool BellmanFord(int s, int n) {
    queue<int> q;
    memset(inq, 0, sizeof inq);
    memset(cnt, 0, sizeof cnt);
    for (int i = 1; i <= n; ++i) d[i] = INF;
    d[s] = 0;
    inq[s] = true;
    q.push(s);

    while (q.size()) {
        int u = q.front(); q.pop();
        inq[u] = false;
        for (int i = 0; i < G[u].size(); ++i) {
            int v = G[u][i].to;
            int c = G[u][i].cost;
            if (d[u] < INF && d[v] > d[u] + c) {
                d[v] = d[u] + c;
                pre[v] = u;
                if (!inq[v]) {
                    q.push(v); inq[v] = true;
                    if (++cnt[v] > n) return false; // 存在负环
                }
            }
        }
    }
    return true;
}

poj1847 Tram

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
const int INF = 0x5f5f5f5f;
const int N = 105;
struct Edge {
    int to, cost;
};
vector<Edge> G[N];
int d[N];
bool inq[N];
bool BellmanFord(int s, int n) {
    queue<int> q;
    memset(inq, 0, sizeof inq);
    for (int i = 1; i <= n; ++i) d[i] = INF;
    d[s] = 0;
    inq[s] = true;
    q.push(s);

    while (q.size()) {
        int u = q.front(); q.pop();
        inq[u] = false;
        for (int i = 0; i < G[u].size(); ++i) {
            int v = G[u][i].to;
            int c = G[u][i].cost;
            if (d[u] < INF && d[v] > d[u] + c) {
                d[v] = d[u] + c;
                if (!inq[v]) {
                    q.push(v); inq[v] = true;
                }
            }
        }
    }
    return true;
}

int main()
{
//freopen("in.txt", "r", stdin);
    int n, s, t;
    while (~scanf("%d%d%d", &n, &s, &t)) {
        for (int i = 0; i <= n; ++i) G[i].clear();
        int cnt, v;
        for (int i = 1; i <= n; ++i) {
            scanf("%d", &cnt);
            for (int j = 0; j < cnt; ++j) {
                scanf("%d", &v);
                G[i].push_back(Edge{v, j!=0});
            }
        }
        BellmanFord(s, n);
        printf("%d\n", d[t]==INF ? -1 : d[t]);
    }
    return 0;
}

时间: 2024-08-28 03:48:43

Bellman-Ford算法的相关文章

Bellman - Ford 算法解决最短路径问题

Bellman - Ford 算法: 一:基本算法 对于单源最短路径问题,上一篇文章中介绍了 Dijkstra 算法,但是由于 Dijkstra 算法局限于解决非负权的最短路径问题,对于带负权的图就力不从心了,而Bellman - Ford算法可以解决这种问题. Bellman - Ford 算法可以处理路径权值为负数时的单源最短路径问题.设想可以从图中找到一个环路且这个环路中所有路径的权值之和为负.那么通过这个环路,环路中任意两点的最短路径就可以无穷小下去.如果不处理这个负环路,程序就会永远运

Bellman—Ford算法思想

---恢复内容开始--- Bellman—Ford算法能在更普遍的情况下(存在负权边)解决单源点最短路径问题.对于给定的带权(有向或无向)图G=(V,E),其源点为s,加权函数w是边集E的映射.对图G运行Bellman—Ford算法的结果是一个布尔值,表明图中是否存在着一个从源点s可达的负权回路.若存在负权回路,单源点最短路径问题无解:若不存在这样的回路,算法将给出从源点s到图G的任意顶点v的最短路径值d[v] Bellman—Ford算法流程 分为三个阶段: (1)初始化:将除源点外的所有顶点

poj 3259 Wormholes (BELLman—FOrd算法)(邻接矩阵表示)

http://poj.org/problem?id=3259 之前一开始 ,没做出来,搁置了好几天才看见bug所在.所以今天a掉了 ,把代码贴出来,这个使用邻接矩阵表示的 ,下一篇用邻接表可以提高效率. #include<iostream> #include<queue> #include<stdio.h> #include<string.h> using namespace std; const int INF=600; int G[INF][INF];

最短路径——Bellman Ford算法(C++)

源代码: #include<cstdio>#include<cstring>int m(1),n,k,i[1001],x[1001],y[1001],f[1001];int main(){ scanf("%d%d",&n,&k); for (int a=1;a<=n;a++) for (int b=1;b<=n;b++) { scanf("%d",&i[m]); if (i[m]!=-1) { x[m]=a

POJ 1860 Currency Exchange (Bellman ford)

Currency Exchange Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 22405   Accepted: 8095 Description Several currency exchange points are working in our city. Let us suppose that each point specializes in two particular currencies and pe

bellman-ford算法

给定图G(V, E)(其中V.E分别为图G的顶点集与边集),源点s, 数组Distant[i]记录从源点s到顶点i的路径长度,初始化数组Distant[n]为, 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进行更新,说明最短路径已经查找完

ACM/ICPC 之 最短路径-Bellman Ford范例(POJ1556-POJ2240)

两道Bellman Ford解最短路的范例,Bellman Ford只是一种最短路的方法,两道都可以用dijkstra, SPFA做. Bellman Ford解法是将每条边遍历一次,遍历一次所有边可以求得一点到任意一点经过一条边的最短路,遍历两次可以求得一点到任意一点经过两条边的最短路...如 此反复,当遍历m次所有边后,则可以求得一点到任意一点经过m条边后的最短路(有点类似离散数学中邻接矩阵的连通性判定) POJ1556-The Doors 初学就先看POJ2240吧 题意:求从(0,5)到

Bellman ford 最短路径算法

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" /> 下表记录S到每一个节点的距离: 第一次迭代, S->A = 4 ,由于S->A眼下为INF.因此更新MIN(S->A)为4 S->B = 6.由于S->B眼下为INF.因此更新MIN(S->B)为6 S->C

poj1860 bellman—ford队列优化 Currency Exchange

Currency Exchange Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 22123   Accepted: 7990 Description Several currency exchange points are working in our city. Let us suppose that each point specializes in two particular currencies and pe

Ford算法(单源最短路径)

优点:代码较少,复杂度不高,可以判断是否会有负环. 缺点:效率低. 算法阐述: 这个算法的思想非常简单,首先它是根据从起点向与它相连的线段开始刷新,只要满足刷新后的路径比原有路径小的话,那么就立即更新这个数据,是这个数据作为新的数 据.同时这个算法有一个很重要的优势,那就是可以判断有没有负环的存在. 负环判断原理: 这个算法的代码我在下面会有一个代码的描述,这个算法是通过一个整体的更新来实现查找最短的路径,那么这里面就有一个关于这个算法的更新次数的问题,首先一个确定 下来的顶点它向它周围进行一个