zoukankan      html  css  js  c++  java
  • 图的路径:最短路-四种算法

           先说前三种,主要解决的问题是:“求a点到b点的最短路”,解决方法是:用以下三种算法中的一种,求出a点到其他所有点的最短路,即可知道a点到b点的最短路。

           前三种最短路算法是:Dijkstra(及优先队列优化),Bellman-Ford,SPFA(实际上就是BF的优化版)。


    Dijkstra算法: (要求边权只能大于等于0)

    未优化 O(N2):

           贪心算法:每次找到当前距离起始点(a点)最短的点扩展更新,且该点不会再被更新

           数据结构:开一个二维数组cost[i][j]来存储各点之间的距离(邻接矩阵),INF表示无通路,x点到x点之间距离为0;用一维数组lowcost[i]表示最短路径,用vis[i]保存路径中某点是否已经走过。

    Dij+邻接矩阵 模板如下:

    const int INF=10e7;
    const int MAXN=1010;
    int k,minn;
    int cost[MAXN][MAXN];
    int lowcost[MAXN];
    bool vis[MAXN];
    
    void dij(int n,int start)
    {
        for(int i=1;i<=n;i++)
            lowcost[i]=INF,vis[i]=0;
        lowcost[start]=0;
    
        for(int j=1;j<=n;j++)
        {
            k=-1,minn=INF;
            for(int i=1;i<=n;i++)
            {
                if(!vis[i]&&lowcost[i]<minn)
                    {minn=lowcost[i];k=i;}
            }
            if(k==-1) break; //这里需要对k进行判定,是因为有可能在k未赋值的情况下使用了vis[k]
            vis[k]=1;
            for(int i=1;i<=n;i++)
                if(!vis[i]&&cost[k][i]>=0&&lowcost[k]+cost[k][i]<lowcost[i])
                    lowcost[i]=lowcost[k]+cost[k][i];
        }
    }
     


    优先队列优化 O(NlogN):    //且对于稠密图,使用优先队列优化的Dijkstra算法更有效

           将原先未优化版的第一个O(N)优化,用优先队列来维护,复杂度变为O(logN),故总复杂度为O(NlogN)。

    Dij+邻接表+优先队列 模板如下:

    const int MAXN=1010;
    const int INF=10e8;
    
    struct node
    {
        int v,val;
        node(int _v=0,int _val=0): v(_v),val(_val) {}
        //等价于node(int _v,int _val) {v=_v;val=_val;}
        bool operator < (const node &a) const
        {
            if(val==a.val) return v<a.v;
            else return val>a.val;
        }
    };
    
    vector<node> E[MAXN];
    bool vis[MAXN];
    
    void Dijkstra(int lowcost[],int n,int start)
    {
        int len,u,v,cost;
        node qtemp;
        for(int i=1;i<=n;i++)
            lowcost[i]=INF;vis[i]=0;
        lowcost[start]=0;
        //用优先队列优化
        priority_queue<node> que;
        que.push(node(start,0));
        while(!que.empty())  //类似于BFS
        {
            qtemp=que.top();que.pop();
            u=qtemp.v;
            if(vis[u])
                continue;
            vis[u]=1;len=E[u].size();
            
            for(int i=0;i<len;i++)
            {
                v=E[u][i].v;cost=E[u][i].cost;
                if(!vis[v]&&lowcost[v]>lowcost[u]+cost)
                {
                    lowcost[v]=lowcost[u]+cost;
                    que.push(node(v,lowcost[v]));
                }
            }
        }
    }


    Bellman-Ford算法(边权可以为负权,且可用于判断图是否存在负权回路)

        原理是进行松弛操作,最多N-1次,复杂度O(NM)。(松弛一次的复杂度为O(M))

    步骤:

           1.初始化所有点,每个点保存一个值,表示从原点到此点的距离,INF表示两点之间不连通;

           2.进行循环,下标从1到n-1(n为图中点的个数),复杂度O(N),在循环内部遍历所有的边,进行松弛,复杂度O(M);

           3.跳出循环后,遍历途中所有的边(edge(u,v)),判断是否存在:

         lowcost(v)>lowcost(u)+cost(u,v)的情况,若存在,则返回false,表示图中存在负权回路,无法求得最短路径。

    BF+邻接表 模板如下:

    const int MAXN=1010;
    const int INF=10e8;
    
    struct edge
    {
        int u,v,cost;
        edge(int _u=0,int _v=0,int _cost=0) u(_u),v(_v),cost(_cost) {}
    };
    
    vector<edge> E;
    
    bool Bellman_Ford(int lowcost[],int n,int start)
    { //返回值用于判断是否有负环,false说明有负环,即无法求出最短路
        bool ok;
        int u,v,cost;
        int len=E.size();
        
        for(int i=1;i<=n;i++)
            lowcost[i]=INF;
        lowcost[start]=0;
        
        for(int i=0;i<n-1;i++)
        {
            ok=0;
            for(int j=0;j<len;j++)
            {
                u=E[j].u;v=E[j].v;cost=E[j].cost;
                if(lowcost[v]>lowcost[u]+cost)  //进行松弛
                {
                    lowcost[v]=lowcost[u]+cost;
                    ok=1;
                }
            }
            if(!ok)
                return true;
        }
        for(int j=0;j<len;j++)
            if(lowcost[E[j].v]>lowcost[E[j].u]+E[j].cost)
                return false;
        return true;
    }
    
    //加边操作
    inline void add_edge(int u,int v,int c)
    {
        E.push_back(edge(u,v,c));
    }


    SPFA算法:

        是优化后的BF算法,在进行松弛的时候,每次只对上一次改变过的点进行松弛。

    SPFA+邻接表 模板如下:

    const int MAXN=1010;
    const int INF=10e8;
    
    struct edge
    {
        int v,cost;
        edge(int _v=0,int _cost=0) v(_v),cost(_cost) {}
    };
    
    vector<edge> E[MAXN];
    bool vis[MAXN];
    int couNode[MAXN];
    
    bool SPFA(int lowcost[],int n,int start)
    { //返回值用于判断是否有负环,false说明有负环,即无法求出最短路
        queue<int> que;
        int u,v,cost;
        int len=E.size();
        
        for(int i=1;i<=n;i++)
            lowcost[i]=INF,vis[i]=0,couNode[i]=0;
        lowcost[start]=0;vis[start]=0;couNode[start]=1;
        que.push(start);
        
        for(!que.empty())
        {
            u=que.front();que.pop();
            vis[u]=0;len=E[u].size();
            
            for(int i=0;i<len;i++)
            {
                v=E[i].v;cost=E[i].cost;
                if(lowcost[v]>lowcost[u]+cost)  //进行松弛
                {
                    lowcost[v]=lowcost[u]+cost;
                    if(!vis[v])
                    {
                        vis[v]=1;
                        ++couNode[v];
                        que.push(v);
                        if(couNode[v]>n)
                            return false;
                    }
                }
            }
        }
        return true;
    }
    
    //加边操作
    inline void add_edge(int u,int v,int c)
    {
        E[u].push_back(edge(v,c));
    }


    Floyd算法:

          第四种比较特殊又好用的算法是folyd算法;

          若要求图上任意两点之间的最短路(而不是给了确切的起点a),就用Folyd算法;此算法代码短,复杂度O(N3)用的是动态规划思想。

          且Floyd算法也可用于判环,若lowcost[a][a]有值且不为INF,那么说明图中存在从a点到a点的一个环。

    Floyd+邻接矩阵 模板如下:

    const int INF=10e7;
    const int MAXN=1010;
    int lowcost[MAXN][MAXN];  //记录a点到b点之间的最短路
    
    void floyd(int n)
    {
        for(int k=1;k<=n;k++)  //千万注意k要在最外层循环,否则会出错
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    lowcost[i][j]=min(lowcost[i][j],lowcost[i][k]+lowcost[k][j]);
    }
  • 相关阅读:
    6、linux中同步、互斥、阻塞(原子操作、信号量、阻塞)
    lightOJ-1199 Partitioning Game (SG函数)
    HDU-1013 Digital Roots
    HDU-1004 Let the Balloon Rise (STL map)
    HDU-1020 Encoding (字符串)
    POJ-2524 Ubiquitous Religions (并查集)
    POJ-1988 Cube Stacking (带权并查集)
    POJ-2236 Wireless Network (并查集)
    HDU-1002 A + B Problem II (模拟大数相加)
    HDU-1829 A Bug's Life (种类并查集)
  • 原文地址:https://www.cnblogs.com/atmacmer/p/5177823.html
Copyright © 2011-2022 走看看