最短路的Bellman-Ford算法 【判断有无负权环】

来源:互联网 发布:框架户和端口户的返点 编辑:程序博客网 时间:2024/05/17 08:15

Bellman-Ford算法是一种求单源最短路算法,时间复杂度:O(V E)V为图的节点数,E为图的边数),效率很低,但比起dijkstra算法,它可以处理负权边,而且能判断源点是否有负权环(floyd算法只能求最短路不能判断有无),即从源点经过一段路后回到原点有无负权和。

过程:

BELLMAN-FORD(G, w, s)1  INITIALIZE-SINGLE-SOURCE(G, s)2  for i1 to |V[G]| - 13       do for each edge (u, v) ∈ E[G]4              do RELAX(u, v, w)5  for each edge (u, v) ∈ E[G]6       do if d[v] > d[u] + w(u, v)7             then return FALSE8  return TRUE

for(节点数-1)                      // 跟 dijkstra一样      对每条边松弛for(每条边)     if(可以松弛)          return 存在负边return 无负边


常用一个数组dis[]表示源点到各点距离

结构体{int u;         // 起点int v;         // 终点int w;        // 边权}edge[]表示边的信息


松弛部分很好理解,源点到某一边的终点距离 小于 源点到该边的起点距离  + 起点到终点距离 就更新 到终点距离

if( dis[edge[j].u] + edge[j].w < dis[edge[j].v] ){    dis[edge[j].v] = dis[edge[j].u] + edge[j].w;}


下面看POJ 3259 例题

题意:有F个农场(测试数据组数),N个田(节点),M条双向路径(双/无向正权边),W个虫洞(单向负权边),问能否遇见原先的自己(负权环)

这题输入部分

输入F;while(F--){    输入N,M,W;    1 -> M        加边入图(双向边就当成两条边处理)    1 -> W        负权边入图(注意负权)    进入算法}
还可以用标记变量优化,见代码
#include<cstdio>#include<cmath>#include<cstring>#include<iostream>#include<algorithm>using namespace std;#define ll long long#define NMAX 2502#define INF 0x7F7F7F7F#define eps 10^(-6)#define MEM(a) memset(a,0,sizeof(a));#define MEM_MAX(a) memset(a,INF,sizeof(a));#define FOR(i,n) for(int i=0;i<n;i++)#define FIN freopen("in.txt","r",stdin);#define FOUT freopen("out.txt","w",stdout);struct node{    int u,v,w;      //边的信息}edge[NMAX*2+201];int dis[NMAX];     //源点到各点距离int N,M,W;         //N:节点数,M:正权双向边数,W:负权边数int cnt;           //总边数:不等于M+W,因为双向边要算两条边int bellman_ford(int src){    for(int i=1;i<=N;i++)        dis[i] = INF;    dis[src] = 0;    for(int i=1;i<N;i++)    {        bool flag = false;        for(int j=1;j<=cnt-1;j++)        {            if( dis[edge[j].u] + edge[j].w < dis[edge[j].v] )            {               dis[edge[j].v] = dis[edge[j].u] + edge[j].w;               flag = true;             }        }        if(!flag)   //优化:如果没一条边更新,则最短路完成或有边不可达            break;    }    for(int i=1;i<=cnt-1;i++)        if(dis[edge[i].u] + edge[i].w < dis[edge[i].v])            return 0;    return 1;}int main(){    int u,v,w,f,M,W;    scanf("%d",&f);    while(f--)    {        cnt = 1;        scanf("%d%d%d",&N,&M,&W);        for(int i=1;i<=M;i++)        {            scanf("%d%d%d",&u,&v,&w);            edge[cnt].u = u;            edge[cnt].v = v;            edge[cnt++].w = w;            edge[cnt].v = u;            edge[cnt].u = v;            edge[cnt++].w = w;        }        for(int i=1;i<=W;i++)        {            scanf("%d%d%d",&u,&v,&w);            edge[cnt].u = u;            edge[cnt].v = v;            edge[cnt++].w = -w; //负权        }        if(bellman_ford(1))            printf("NO\n");        else            printf("YES\n");    }    return 0;}


此题的spfa解法

#include <cstdio>#include <cstring>#include <string>#include <stack>#include <queue>#include <iostream>#include <algorithm>using namespace std;const int maxn = 505;const int maxm = 2600;const int inf = 0x3f3f3f3f;int inq[maxn], head[maxn], dis[maxn];   //inq[u]==1:u在队列里struct Edge{    int v, w, next;} edge[maxm * 2];int cnt;void add_edge(int u, int v, int w){ //邻接表前插法    edge[cnt].v = v; edge[cnt].w = w; edge[cnt].next = head[u]; head[u] = cnt++;}int times[maxn];void init(int n){    cnt = 0;    memset(head, -1, sizeof(head));    memset(inq, 0, sizeof(inq));    memset(dis, inf, sizeof(dis));    memset(times, 0, sizeof(times));}int n;bool spfa(int s, int t){    queue<int>q;    q.push(s);    dis[s] = 0;    inq[s] = 1;    times[s]++;    while (!q.empty()){        int u = q.front(); q.pop();        inq[u] = 0;        for (int i = head[u]; i != -1; i = edge[i].next) {            int v = edge[i].v;            int w = edge[i].w;            if (dis[v] > dis[u] + w){                dis[v] = dis[u] + w;                if (!inq[v]){                    inq[v] = 1;                    q.push(v);                    times[v]++;                    if(times[v] > n){                        return false;                    }                }            }        }    }    return true;}int main() {    int  m, a, b, c;    int te;    cin>>te;    int qq;    while(te--){        cin >> n >> m>>qq;        init(n);        for (int i = 0; i < m; ++i){            cin >> a >> b >> c;            add_edge(a, b, c);            add_edge(b, a, c);        }        for (int i = 0; i < qq; ++i){            cin >> a >> b >> c;            add_edge(a, b, -c);        }        if(spfa(1, n))            cout<<"NO"<<endl;        else            cout<<"YES"<<endl;    }    return 0;}




0 0
原创粉丝点击