最短路之Bellman-Ford算法

Bellman-Ford算法通过对边进行松弛操作来渐进地降低从源结点到其他结点的最短路径
每次循环:对所有的边进行松弛操作
循环次数:共循环n-1次(n为顶点数)
故时间复杂度: O(VE)

/* Bellman-Ford算法 --- 以边为存储结构 */
#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
using namespace std;

const int maxn = 100;
const int INF = 65535;

struct Edge{
    int from, to, dist;
    Edge(){}
    Edge(int a, int b, int c) :from(a), to(b), dist(c){}
};

vector<int> G[maxn]; //G[i]-->以i为起点的边
vector<Edge> edges;  //
int inq[maxn], cnt[maxn];
int d[maxn], pre[maxn];
bool visit[maxn];
int n, m;

/************************************************************************
  Bellman-Ford算法通过对边进行松弛操作来渐进地降低从源结点到其他结点的最短路径
  每次循环:对所有的边进行松弛操作
  循环次数:共循环n-1次(n为顶点数)
  故时间复杂度: O(VE)
 ************************************************************************/
bool bellman_ford2(int v0){
    for (int i = 0; i < n; ++i)
        d[i] = INF;
    d[v0] = 0;
    for (int i = 0; i < n; ++i){
        //对所有的边进行松弛
        for (int j = 0; j < edges.size(); ++j){
            int x = edges[j].from;
            int y = edges[j].to;
            int w = edges[j].dist;
            if (d[x] + w < d[y]){
                d[y] = d[x] + w;
                pre[y] = x;
            }
        }
    }
    /* 判断是否存在负环 */
    for (int j = 0; j < edges.size(); ++j){
        Edge tmp = edges[j];
        if (d[tmp.from] + tmp.dist < d[tmp.to])
            return false;
    }
    return true;
}

/* 利用队列代替循环实现 */
bool bellman_ford(int s){
    queue<int> Q;  //Q是存储结点的队列
    memset(inq, 0, sizeof inq);
    memset(cnt, 0, sizeof cnt);

    for (int i = 0; i < n; ++i)
        d[i] = INF;
    d[s] = 0;
    inq[s] = true;
    Q.push(s);

    while (!Q.empty()){
        int u = Q.front(); Q.pop();
        inq[u] = false;
        for (int i = 0; i < G[u].size(); ++i){
            //对边进行松弛操作
            Edge& e = edges[G[u][i]];
            if (d[u] < INF && d[e.to] > d[u] + e.dist){
                d[e.to] = d[u] + e.dist;
                pre[e.to] = u;
                if (!inq[e.to]){
                    Q.push(e.to);
                    inq[e.to] = true;
                    //若对结点的访问次数大于n则存在负环
                    if (++cnt[e.to] > n){
                        return false;
                    }
                }
            }
        }
    }
    return true;
}

int main()
{
    int u, v, w, k;
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; ++i){
        scanf("%d%d%d", &u, &v, &w);
        edges.push_back(Edge(u, v, w));
        k = edges.size();
        G[u].push_back(k - 1);
        //有向图可需要再加上这3行
        //edges.push_back(Edge(v, u, w));
        //k = edges.size();
        //G[v].push_back(k - 1);
    }
    if(bellman_ford(0)){
        for (int i = 0; i < n; ++i){
            printf("%d  ", d[i]);
        }
    }
    printf("\n");
    //0-1的路径的反序
    k = 1;
    while (k != 0){
        printf("%3d ", k);
        k = pre[k];
    }
    printf("%3d\n", k);

    ////以边为存储结构的输出
    //while (k != 0){
    //    printf("%3d", k);
    //    k = edges[pre[k]].from;
    //}
    //printf("%3d\n", k);

    return 0;
}

/*

带负权的图
5 10
0 1 6
0 3 7
1 2 5
1 3 8
1 4 -4
2 1 -2
3 2 -3
3 4 9
4 0 2
4 2 7

0到其他点的最短路(带负环则结束)
    dis ---> 0 2 4 7 -2

*/

时间: 2024-11-05 12:34:14

最短路之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)到

关于最短路的几个算法

关于最短路的几个算法有Dijkstra,Bellman-Ford,Floyd Dijkstra: Dijkstra适用于边权为正的情况,从单个源点出发,到其他所有结点的最短路 算法的核心是用已经知道的结点 i 的距离 d[i] 去更新和这个结点相连的其他结点的距离 void Dijkstra() { memset(vis,0,sizeof(vis)); //vis数组表示结点是否被访问 memset(d,INF,sizeof(d)); //d数组表示到结点的距离 d[s]=0; //将起点的距离

[poj2449]Remmarguts&#39; Date(K短路模板题,A*算法)

解题关键:k短路模板题,A*算法解决. #include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> #include<iostream> #include<cmath> #include<queue> using namespace std; typedef long long ll; const int N=1e3+10; const

单源最短路 狄克斯特拉算法

一般形式的用邻接矩阵来实现dijkstra效率比较低,我这里直接记录的是用邻接表的方法以及用优先队列加以应用. 首先解释什么是dijkstra算法 dijkstra算法 dijkstra算法适用于求单源最短路,即可以求出起点到其余各点之间的最短路.它的算法实现是一个不断更新的过程. 举一个最简单的例子,假设有这么一个图,红色表示权值,黑色表示4个点以及路径,我们假设起点为1,用d[i]来表示1到i的最短路,那么在第一轮的时候,d[2]=1,d[3]=1,d[4]=5,再下一轮的时候会对这个情况进