zoukankan      html  css  js  c++  java
  • SPFA算法

    SPFA算法

    解决问题

    • 单源点最短路径
    • 可处理负权边
    • 判断负环

    Bellman-Ford算法

    • SPFA算法是Bellman-Ford的优化版本

    • Bellman-Ford算法流程

      • 初始化:将除源点外的所有顶点的最短距离估计值 d[v] ←+∞, d[s] ←0;
      • 迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V中的每个顶点v的最短距离估计值逐步逼近其最短距离;(运行|v|-1次)
      • 检验负权回路:判断边集E中的每一条边的两个端点是否收敛。如果存在未收敛的顶点,则算法返回false,表明问题无解;否则算法返回true,并且从源点可达的顶点v的最短距离保存在 d[v]中。
    • 算法伪码

    for each vertex v∈V do
        dis[v] = ∞
    dis[s] = 0
    for i=1 to |v| - 1 do
        for each edge(u,v) ∈ E do
            if(dis[u] + w(u,v) < dis[v]) then
                dis[v] = dis[u] + w(u,v)
    for each edge(u,v) ∈ E do
        if(dis[v] > dis[u] + w(u,v)) then
            return false
    return true
    
    
    • 源码
    
    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define MAX 0x3f3f3f3f
    #define N 1010
    int nodenum, edgenum, original; //点,边,起点
    typedef struct Edge //边
    {
        int u, v;
        int cost;
    } Edge;
    Edge edge[N];
    int dis[N], pre[N];
    bool Bellman_Ford()
    {
        for(int i = 1; i <= nodenum; ++i) //初始化
            dis[i] = (i == original ? 0 : MAX);
        for(int i = 1; i <= nodenum - 1; ++i)
            for(int j = 1; j <= edgenum; ++j)
                if(dis[edge[j].v] > dis[edge[j].u] + edge[j].cost) //松弛(顺序一定不能反~)
                {
                    dis[edge[j].v] = dis[edge[j].u] + edge[j].cost;
                    pre[edge[j].v] = edge[j].u;
                }
        bool flag = 1; //判断是否含有负权回路
        for(int i = 1; i <= edgenum; ++i)
            if(dis[edge[i].v] > dis[edge[i].u] + edge[i].cost)
            {
                flag = 0;
                break;
            }
        return flag;
    }
     
    void print_path(int root) //打印最短路的路径(反向)
    {
        while(root != pre[root]) //前驱
        {
            printf("%d-->", root);
            root = pre[root];
        }
        if(root == pre[root])
            printf("%d
    ", root);
    }
     
    int main()
    {
        scanf("%d%d%d", &nodenum, &edgenum, &original);
        pre[original] = original;
        for(int i = 1; i <= edgenum; ++i)
        {
            scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].cost);
        }
        if(Bellman_Ford())
            for(int i = 1; i <= nodenum; ++i) //每个点最短路
            {
                printf("%d
    ", dis[i]);
                printf("Path:");
                print_path(i);
            }
        else
            printf("have negative circle
    ");
        return 0;
    }
    
    • 主要思想

      • for i=1 to |v| - 1 相当于逐层生成最短路径树
      • 任意最短路径必然最多只包含|v|-1条边 只需循环这么多次
      • 事实上,每次实施一层松弛,经过i条边所到达的所有最短路径已经生成,不受后续松弛操作的影响。但是后续每次都还要判断
    • SPFA

      • 正是解决了Bellman-Fort算法在冗余松弛操作上的不足
      • 算法流程
        • 维护一个队列,每次进行一次类似BFS的操作实际上就是生成了该层的最短路径树
        • 每次取出队首结点u,并且用u点当前的最短路径估计值对u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。
    
    #include<iostream>
    #include<queue>
    #include<list>
    #include<vector>
    #include<cstring>
    #include<set>
    #include<stack>
    #include<map>
    #include<cmath>
    #include<algorithm>
    #include<string>
    #include<stdio.h>
    using namespace std;
    typedef long long ll;
    #define MS(x,i) memset(x,i,sizeof(x))
    #define rep(i,s,e) for(int i=s; i<=e; i++)
    #define sc(a) scanf("%d",&a)
    #define scl(a) scanf("%lld",&a)
    #define sc2(a,b) scanf("%d %d", &a, &b)
    #define debug printf("debug......
    ");
    #define pfd(x) printf("%d
    ",x)
    #define pfl(x) printf("%lld
    ",x)
    const double eps=1e-8;
    const double PI = acos(-1.0);
    const int inf = 0x3f3f3f3f;
    const ll INF = 0x7fffffff;
    const int maxn = 2e2+10;
    int dx[4] = {0, 0, 1, -1};
    int dy[4]  = {1, -1, 0 , 0};
    
    const int M = 1e4+10;
    
    int n,m;//顶点数边数
    int head[maxn];//记录顶点的第一条边
    int cnt; //记录当前是第几条边
    //边
    struct node{
        int to;
        int w;
        int nxt;
    }edge[2*M]; //无向图的话要开两倍
    //加边
    void addEdge(int u, int v, int w){
        edge[cnt].to = v;
        edge[cnt].w = w;
        edge[cnt].nxt = head[u];
        head[u] = cnt++;
    }
    
    bool vis[maxn];//记录是否在队列
    int dis[maxn];//记录当前最短距离
    int in[maxn];//记录结点被入队了多少次,如果大于n则说明有负环
    //int path[maxn];//记录前驱
    bool SPFA(int s){
        //初始化
        rep(i,1,n){
            vis[i] = 0;
            dis[i] = inf;
            in[i] = 0;
        }
        queue<int> q;
        q.push(s);
        vis[s] = 1;
        dis[s] = 0;//到自己的距离为0
        in[s] = 1;
        while(!q.empty()){
            int fr = q.front();
            q.pop();
            vis[fr] = 0;//出队后置为0
            //对每个与fr相连的顶点 对其进行松弛操作
            for(int i=head[fr]; i!=0; i=edge[i].nxt){
                int v = edge[i].to;
                int w = edge[i].w;
                if(dis[v] > dis[fr] + w){
                    //pre[v] = fr;
                    dis[v] = dis[fr] +  w;
                    if(!vis[v]){
                        q.push(v);
                        in[v]++;
                        vis[v] = 1;
                        if(in[v] > n) return false;
                    }
                }
            }
        }
        return true;
    }
    
    int main(){
        while(cin>>n>>m){
            if(n==0 && m == 0) break;
            int u,v,w;
            cnt = 1;//第一条边
            MS(head , 0 );
            rep(i , 1, m){
                cin>>u>>v>>w;
                addEdge(u , v , w);
                addEdge(v , u , w);
    
            }
            int s;
            s = 1;
            if(SPFA(s)) cout<<dis[n]<<endl;
            else cout<<"-1"<<endl;
        }
        return 0;
    }
    
    
  • 相关阅读:
    【复习+知识补充】EL表达式:只能调用静态方法
    【复习】sql语句的拼接 + 链接地址的简写
    淘淘商城maven工程的创建和svn的上传实现
    淘淘商城基于maven和svn的理解
    国家电力项目SSH搭建
    linux中权限的修改
    chown -R命令的使用
    修改nginx的访问目录以及遇到的403错误修改总结
    nginx的在linux系统中的安装
    集群环境的图片的访问和存储
  • 原文地址:https://www.cnblogs.com/czsharecode/p/10716341.html
Copyright © 2011-2022 走看看