zoukankan      html  css  js  c++  java
  • 最短路(代码来源于kuangbin和百度)

    最短路

    最短路有多种算法,常见的有一下几种:Dijstra、Floyd、Bellman-Ford,其中Dijstra和Bellman-Ford还有优化;Dijstra可以用优先队列(或者堆)优化,Bellman-Ford也可以用队列优化,通常称为spfa。下面分别对这几种算法进行说明。

    Dijstra适用于没有负权边的图,Bellman-Ford适用于有负权边的图,但是不能得到有负环的图的最短距离,只能判断有没有负环。Dijstra和Bellman-Ford都是单源最短路,Floyd算法是多源最短路。

    一、Dijstra算法

    Dijstra算法是不断将新的距离原点最短的点加入已经得到最短距离的集合,然后每加入一个点都更新各个点到源点的距离。这里有一个问题,设已经的到最短距离的点的集合为V,如何保证已经加入V的点已经得到了最短距离而之后源点到V中的点的最短距离不会改变了呢?假设i点已经加入V,而j点后来加入V,因为所有的边权值都是正的,如果存在dis[j]+cost[j][i]<dis[i](dis[i]表示i点到源点的距离,cost[i][j]表示i点到j点的边的权值),那么dis[j]<dis[i],如果dis[j]<dis[i],j点就应该先于i点加入V,与已知条件不符。所以在i点加入V之后,不会存在一个点作为中间点使得源点到i点的距离最小。

    Dijstra算法的过程如下:

     

    代码:

    /*
    *  单源最短路径,Dijkstra算法,邻接矩阵形式,复杂度为O(n^2)
    *  求出源beg到所有点的最短路径,传入图的顶点数,和邻接矩阵cost[][]
    *  返回各点的最短路径lowcost[],  路径pre[].pre[i]记录beg到i路径上的父结点, pre[beg]=-1
    *  可更改路径权类型,但是权值必须为非负
    *
    */
    const int MAXN=1010;
    #define typec int
    const typec INF=0x3f3f3f3f;//防止后面溢出,这个不能太大
    bool vis[MAXN];
    int pre[MAXN];
    void Dijkstra(typec cost[][MAXN],typec lowcost[],int n,int beg)
    {
        for(int i=0;i<n;i++)
        {
            lowcost[i]=INF;
            vis[i]=false;
            pre[i]=-1;
        }
        lowcost[beg]=0;
        for(int j=0;j<n;j++)
        {
            int k=-1;
            int Min=INF;
            for(int i=0;i<n;i++)
                if(!vis[i]&&lowcost[i]<Min)
                {
                    Min=lowcost[i];
                    k=i;
                }
            if(k==-1)
                break;
            vis[k]=true;
            for(int i=0;i<n;i++)
                if(!vis[i]&&lowcost[k]+cost[k][i]<lowcost[i])
                {
                    lowcost[i]=lowcost[k]+cost[k][i];
                    pre[i]=k;
                }
        }
    }
    View Code

    二、Dijstra+优先队列

    将每次选取到达源点最近点的过程用一个优先队列代替,直接出队得到最近点和距离。但要把已经处理过的点涉及到的已经入队的略过不处理。

    代码:

    /*
    *  使用优先队列优化Dijkstra算法
    *  复杂度O(ElogE)
    *  注意对vector<Edge>E[MAXN]进行初始化后加边
    */
    const int INF=0x3f3f3f3f;
    const int MAXN=1000010;
    struct qnode
    {
        int v;
        int c;
        qnode(int _v=0,int _c=0):v(_v),c(_c){}
        bool operator <(const qnode &r)const
        {
            return c>r.c;
        }
    };
    struct Edge
    {
        int v,cost;
        Edge(int _v=0,int _cost=0):v(_v),cost(_cost){}
    };
    vector<Edge>E[MAXN];
    bool vis[MAXN];
    int dist[MAXN];
    void Dijkstra(int n,int start)//点的编号从1开始
    {
        memset(vis,false,sizeof(vis));
        for(int i=1;i<=n;i++)
            dist[i]=INF;
        priority_queue<qnode>que;
        while(!que.empty())
            que.pop();
        dist[start]=0;
        que.push(qnode(start,0));
        qnode tmp;
        while(!que.empty())
        {
            tmp=que.top();
            que.pop();
            int u=tmp.v;
            if(vis[u])
                continue;
            vis[u]=true;
            for(int i=0;i<E[u].size();i++)
            {
                int v=E[tmp.v][i].v;
                int cost=E[u][i].cost;
                if(!vis[v]&&dist[v]>dist[u]+cost)
                {
                    dist[v]=dist[u]+cost;
                    que.push(qnode(v,dist[v]));
                }
            }
        }
    }
    void addedge(int u,int v,int w)
    {
        E[u].push_back(Edge(v,w));
    }
    View Code

    三、Bellman-Ford算法

    Bellman-Ford算法是对每条边进行松弛,重复|V|次,时间复杂度是O(V*E),伪代码如下:

     for(i = 0; i < |V|; i++)
        for each edge(u, v) ∈  E
            RELAX(u, v)

    算法简单,但是实际中不被采用,因为存在大量的无效松弛。

    代码:

    /*
    *  单源最短路bellman_ford算法,复杂度O(VE)
    *  可以处理负边权图。
    *  可以判断是否存在负环回路。返回true,当且仅当图中不包含从源点可达的负权回路
    * vector<Edge>E;先E.clear()初始化,然后加入所有边
    *  点的编号从1开始(从0开始简单修改就可以了)
    */
    const int INF=0x3f3f3f3f;
    const int MAXN=550;
    int dist[MAXN];
    struct Edge
    {
        int u,v;
        int cost;
        Edge(int _u=0,int _v=0,int _cost=0):u(_u),v(_v),cost(_cost){}
    };
    vector<Edge>E;
    bool bellman_ford(int start,int n)//点的编号从1开始
    {
        for(int i=1;i<=n;i++)dist[i]=INF;
        dist[start]=0;
        for(int i=1;i<n;i++)//最多做n-1次
        {
            bool flag=false;
            for(int j=0;j<E.size();j++)
            {
                int u=E[j].u;
                int v=E[j].v;
                int cost=E[j].cost;
                if(dist[v]>dist[u]+cost)
                {
                    dist[v]=dist[u]+cost;
                    flag=true;
                }
            }
            if(!flag)
                return true;//没有负环回路
        }
        for(int j=0;j<E.size();j++)
            if(dist[E[j].v]>dist[E[j].u]+E[j].cost)
                return false;//有负环回路
        return true;//没有负环回路
    }
    View Code

    四、Spfa

    Spfa是Bellman-Ford算法利用队列的优化,Bellman-Ford算法存在大量的无效松弛,spfa对它进行改进,对于每个点只松弛与这个点相关的边。

    代码:

    /*
    *  单源最短路SPFA
    *  时间复杂度  0(kE)
    *  这个是队列实现,有时候改成栈实现会更加快,很容易修改
    *  这个复杂度是不定的
    */
    const int MAXN=1010;
    const int INF=0x3f3f3f3f;
    struct Edge
    {
    int v;
    int cost;
    Edge(int _v=0,int _cost=0):v(_v),cost(_cost){}
    };
    vector<Edge>E[MAXN];
    void addedge(int u,int v,int w)
    {
    E[u].push_back(Edge(v,w));
    }
    bool vis[MAXN];//在队列标志
    int cnt[MAXN];//每个点的入队列次数
    int dist[MAXN];
    bool SPFA(int start,int n)
    {
        memset(vis,false,sizeof(vis));
        for(int i=1;i<=n;i++)
            dist[i]=INF;
        vis[start]=true;
        dist[start]=0;
        queue<int>que;
        while(!que.empty())
            que.pop();
        que.push(start);
        memset(cnt,0,sizeof(cnt));
        cnt[start]=1;
        while(!que.empty())
        {
            int u=que.front();
            que.pop();
            vis[u]=false;
            for(int i=0;i<E[u].size();i++)
            {
                int v=E[u][i].v;
                if(dist[v]>dist[u]+E[u][i].cost)
                {
                    dist[v]=dist[u]+E[u][i].cost;
                    if(!vis[v])
                    {
                        vis[v]=true;
                        que.push(v);
                        if(++cnt[v]>n)  //cnt[i]为入队列次数,用来判定是否存在负环回路
                            return false;
                    }
                }
            }
        }
        return true;
    }
    View Code

    五、Floyd算法

    Floyd算法是多源最短路,算法是将每对顶点(i,j)之间的所有其他点都进行松弛。

    状态转移方程如下: map[i,j]=min{map[i,k]+map[k,j],map[i,j]};

    代码:

    #include<stdio.h>
    #include<stdlib.h>
    #define max 1000000000
     
    int d[1000][1000],path[1000][1000];
    int main()
    {
        int i,j,k,m,n;
        int x,y,z;
        scanf("%d%d",&n,&m);
         
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++){
                d[i][j]=max;
                path[i][j]=j;
        }
         
        for(i=1;i<=m;i++)
        {
        scanf("%d%d%d",&x,&y,&z);
        d[x][y]=z;
        d[y][x]=z;
        }
         
        for(k=1;k<=n;k++)
            for(i=1;i<=n;i++)
                for(j=1;j<=n;j++)
                {
                    if(d[i][k]+d[k][j]<d[i][j]){
                        d[i][j]=d[i][k]+d[k][j];
                        path[i][j]=path[i][k];
                    }
                }
         
        for(i=1;i<=n;i++)
            for(j=1;j<=i;j++)
              if (i!=j) printf("%d->%d:%d
    ",i,j,d[i][j]);
         
        int f,en;
        scanf("%d%d",&f,&en);
        while (f!=en){
            printf("%d->",f);
            f=path[f][en];
        }
        printf("%d
    ",en);
         
        return 0;
    }
    View Code
  • 相关阅读:
    c#自动更新+安装程序的制作
    VS2013项目受源代码管理向源代码管理注册此项目时出错
    WinDbg配置和使用基础
    InstallShield Limited Edition for Visual Studio 2013 图文教程(教你如何打包.NET程序)
    PowerDesigner 如何生成数据库更新脚本
    用户故事(User Story)
    Troubleshooting Record and Playback issues in Coded UI Test
    Coded UI
    compare two oracle database schemas
    How to: Use Schema Compare to Compare Different Database Definitions
  • 原文地址:https://www.cnblogs.com/vwqv/p/5910297.html
Copyright © 2011-2022 走看看