• SPFA 最短路 带负权边的---- 粗了解


      SPFA(Shortest Path Faster Algorithm)是Bellman-Ford算法的一种队列现,减少了不必要的冗余计算。

    算法大致流程是用一个队列来进行维护。 初始时将源加入队列。 每次从队列中取出一个元素,并对所有与他相邻的点进行松弛,若某个相邻的点松弛成功,则将其入队。 直到队列为空时算法结束。

    这个算法,简单的说就是队列优化的bellman-ford,利用了每个点不会更新次数太多的特点发明的此算法

    SPFA——Shortest Path Faster Algorithm,它可以在O(kE)的时间复杂度内求出源点到其他所有点的最短路径,可以处理负边。SPFA的实现甚至比Dijkstra或者Bellman_Ford还要简单

    SPFA可以处理负权边

    定理: 只要最短路径存在,上述SPFA算法必定能求出最小值。

    证明:

      每次将点放入队尾,都是经过松弛操作达到的。换言之,每次的优化将会有某个点v的最短路径估计值d[v]变小。所以算法的执行会使d越来越小。由于我们假定图中不存在负权回路,所以每个结点都有最短路径值。因此,算法不会无限执行下去,随着d值的逐渐变小,直到到达最短路径值时,算法结束,这时的最短路径估计值就是对应结点的最短路径值。

    期望的时间复杂度O(ke), 其中k为所有顶点进队的平均次数,可以证明k一般小于等于2。

    判断有无负环:

      如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)


    /*********************************************/
    //  d数组类似迪杰斯特拉的dis数组,记录起点到i点的局部最优解
    //  c数组用来记录访问 i 点的次数
    //  vis 记录是否在队列里面
    //  用数组模拟邻接表存图,w数组为权值
    /*********************************************/
    bool spfa_bfs(int s) // s为图的起点
    {
        queue <int> q; //  队列里存点
        memset(d,0x3f,sizeof(d));  
        memset(c,0,sizeof(c));
        memset(vis,0,sizeof(vis));
        q.push(s);
        vis[s]=1;
        c[s]=1;
        d[s]=0;
        //顶点入队vis要做标记,另外要统计顶点的入队次数
        while(!q.empty())
        {
            int x;
            x=q.front();
            q.pop();
            vis[x]=0;
            //队头元素出队,并且消除标记
            for(int k=f[x]; k!=0; k=nnext[k]) //遍历顶点x的邻接表
            {
                int y=v[k];
                if( d[x]+w[k] < d[y]) //如果可以松弛
                {
                    d[y]=d[x]+w[k];  //松弛
                    if(!vis[y])  //顶点y不在队内  不要重复入队列
                    {
                        vis[y]=1;    //标记
                        c[y]++;      //统计次数
                        q.push(y);   //入队
                        if(c[y]>NN)  //超过入队次数上限,说明有负环
                            return false;
                    }
                }
            }
        }
        return true;
    }
    


    给出一道题目,练练手.

                                                     点击打开链接

    本人AC代码:  仅供参考 ,不对的地方请指出


    大致思路:

          用数组模拟邻接表存图,然后直接套模板.

    #include <cstdio>
    #include <cstring>
    #include <cctype>
    #include <cmath>
    #include <set>
    #include <map>
    #include <list>
    #include <queue>
    #include <deque>
    #include <stack>
    #include <string>
    #include <bitset>
    #include <vector>
    #include <iostream>
    #include <algorithm>
    #include <stdlib.h>
    
    using namespace std;
    typedef long long LL;
    const int INF=2e9+1e8;
    const int MOD=1e9+7;
    const int MAX_SIZE=1005;
    
    int first[MAX_SIZE*2],nnext[MAX_SIZE*2];
    int edge[MAX_SIZE*2][3],dist[MAX_SIZE],cnt[MAX_SIZE],vis[MAX_SIZE];
    int V,E;
    bool spfa_bfs(int start)
    {
        memset(vis,0,sizeof(vis));
        memset(cnt,0,sizeof(cnt));
        memset(dist,0xf3,sizeof(dist));
        queue<int>q;
        q.push(start);
        vis[start]=1;
        dist[start]=0;
        while(!q.empty())
        {
            start=q.front();
            q.pop();
            vis[start]=0;
            int k=first[start];
            while(k!=-1)
            {
                int terminal=edge[k][1];
                if(dist[terminal]<dist[start]+edge[k][2])
                {
                    dist[terminal]=dist[start]+edge[k][2];
                    if(!vis[terminal])
                    {
                        q.push(terminal);
                        cnt[terminal]++;
                        vis[terminal]=1;
                        if(cnt[terminal]>V) return false;
                    }
                }
                k=nnext[k];
            }
        }
        return true;
    }
    int main()
    {
        // printf("sb
    ");
        int ncase;
        scanf("%d",&ncase);
        while(ncase--)
        {
            int i,j;
            scanf("%d %d",&V,&E);
            for(i=0; i<=1000; i++)
                first[i]=-1;
            for(i=0; i<E; i++)
            {
                int a,b,c,d,e,val1,val2;
                scanf("%d %d %d %d %d",&a,&b,&c,&d,&e);
                val1=d-c;
                val2=e-c;
                int xiaobiao=(i+1)*2-2;
                edge[xiaobiao][0]=a,edge[xiaobiao][1]=b,edge[xiaobiao][2]=val1;
                nnext[xiaobiao]=first[a];
                first[a]=xiaobiao;
                xiaobiao++;
                edge[xiaobiao][0]=b,edge[xiaobiao][1]=a,edge[xiaobiao][2]=val2;
                nnext[xiaobiao]=first[b];
                first[b]=xiaobiao;
            }
            int ans=spfa_bfs(0);
    //        for(i=0;i<V;i++)
    //            printf("%d ",dist[i]);
    //        printf("
    ");
    
            if(!ans) printf("$$$
    ");
            else printf("%d
    ",dist[V-1]);
        }
        return 0;
    }
    



  • 相关阅读:
    Enterprise Library Policy Injection Application Block 之三:PIAB的扩展—创建自定义CallHandler(提供Source Code下载)
    一首最好听的足球队歌,见证往日的辉煌
    WCF后续之旅(10): 通过WCF Extension实现以对象池的方式创建Service Instance
    WCF后续之旅(9): 通过WCF双向通信实现Session管理[下篇]
    WCF后续之旅(7):通过WCF Extension实现和Enterprise Library Unity Container的集成
    WCF后续之旅(8):通过WCF Extension 实现与MS Enterprise Library Policy Injection Application Block 的集成
    ASP.NET Process Model之二:ASP.NET Http Runtime Pipeline Part II
    WCF后续之旅(9):通过WCF的双向通信实现Session管理[上篇]
    WCF后续之旅(1): WCF是如何通过Binding进行通信的
    Enterprise Library Policy Injection Application Block 之二: PIAB设计和实现原理
  • 原文地址:https://www.cnblogs.com/coded-ream/p/7207993.html
Copyright © 2020-2023  润新知