• 最短路径


    Floyd

    ​ Floyd算法可以处理带有负权边的图,但不能处理带有负权回路的图

    ​ 算法思想:通过引入中间边来松弛两点之间的距离

    ​ 算法核心语句:

    //floyd算法:求图上任意两点之间的最短路径
    for(int k =1;k<=n;++k){
        for(int i=1;i<=n;++i){
            for(int j=1;j<=n;++j){
                if(e[i][j]>e[i][k]+e[k][j]){
                    e[i][j] = e[i][k]+e[k][j];
                }
            }
        }
    }
    

    ​ 示例代码:

    #include<cstdio>
    #include<iostream>
    using namespace std;
    int e[10][10];
    #define inf 99999
    int main()
    {
        int m,n;  //m:边的条数  n:顶点个数
        cin>>m>>n;
        //初始化边
        for(int i=1;i<=n;++i){
            for(int j=1;j<=n;++j){
                if(i==j){
                    e[i][j]=0;
                }else{
                    e[i][j]=inf;
                }
            }
        }
        //设置边
        int t1,t2,val;
        for(int i=0;i<m;++i){
            cin>>t1>>t2>>val;
            e[t1][t2] = val;
        }
        //floyd算法:求图上任意两点之间的最短路径
        for(int k =1;k<=n;++k){
            for(int i=1;i<=n;++i){
                for(int j=1;j<=n;++j){
                    if(e[i][j]>e[i][k]+e[k][j]){
                        e[i][j] = e[i][k]+e[k][j];
                    }
                }
            }
        }
        //输出
        for(int i=1;i<=n;++i){
            for(int j=1;j<=n;++j){
                printf("%10d",e[i][j]);
            }
            printf("
    ");
        }
        return 0;
    }
    

    Dijkstra

    ​ Dijkstra算法:单源最短路径(不能处理带有负权边的图)

    ​ 算法思想:维护一个dis数组,记录着源点到其他顶点的路径。每一次从顶点中选出到源点最短的点,然后更新dis数 组里面的值。直到所有顶点被选完。(贪婪算法)

    ​ 算法核心语句:

    //算法核心语句
    for(int i=1;i<n;++i){
        int min = inf;
        int u;
        //寻找剩余顶点中值最小的顶点
        for(int j=1;j<=n;++j){
            if(book[j]==0&&dis[j]<inf){
                if(dis[j]<min){
                    min = dis[j];
                    u = j;
                }
    
            }
        }
        book[u] = 1;
        for(int i=1;i<=n;++i){
            if(e[u][i]<inf){
                if(dis[i]>dis[u]+e[u][i]){
                    dis[i] = dis[u]+e[u][i];
                }
            }
        }
    }
    

    ​ 示例程序:

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int e[10][10],book[10],dis[10];
    #define inf 99999
    int main(){
        int n,m; //n:顶点个数  m:边的条数
        cin>>n>>m;
        //初始化
        for(int i=1;i<=n;++i){
            for(int j=1;j<=n;++j){
                if(i==j){
                    e[i][j] = 0;
                }
                else{
                    e[i][j] = inf;
                }
            }
        }
        //读入边
        for(int i=0;i<m;++i){
            int t1,t2,val;
            cin>>t1>>t2>>val;
            e[t1][t2] = val;
        }
        int source;
        cout<<"请输入源点:"<<endl;
        cin>>source;
        //初始化dis数组和book数组
        for(int i=1;i<=n;++i)
            dis[i] = e[source][i];
        for(int i=1;i<=n;++i)
            book[i]=0;
        book[source]=1;
        
        //算法核心语句
        for(int i=1;i<n;++i){
            int min = inf;
            int u;
            //寻找剩余顶点中值最小的顶点
            for(int j=1;j<=n;++j){
                if(book[j]==0&&dis[j]<inf){
                    if(dis[j]<min){
                        min = dis[j];
                        u = j;
                    }
                        
                }
            }
            book[u] = 1;
            for(int i=1;i<=n;++i){
                if(e[u][i]<inf){
                    if(dis[i]>dis[u]+e[u][i]){
                        dis[i] = dis[u]+e[u][i];
                    }
                }
            }
        }
        
        
        
        //输出
        for(int i=1;i<=n;++i){
            printf("%5d",dis[i]);
        }
        return 0;
    }
    // 6 9
    // 1 2 1
    // 1 3 12
    // 2 3 9
    // 2 4 3
    // 3 5 5
    // 4 3 4
    // 4 5 13
    // 4 6 15
    // 5 6 4
    
    //数组实现邻接表存储图:适用于稀疏图
    int n,m;
    
    int u[6],v[6],w[6]; //用于存储起点、终点、权值,大小要比边数m大
    
    int first[5],next[6]; //first用来存储起始点相同的边的第一条边 ,next用来保存起点相同的边的下一条边的编号
    
    for(int i=1;i<=n;++i)
        first[i] = -1;
    for(int i=1;i<=m;++i){
        scanf("%d %d %d",&u[i],&v[i],&w[i]);  //读入每一条边
        next[i] = first[u[i]];
        first[u[i]] = i;
    }
    
    //遍历邻接表
    for(int i=1;i<=n;++i){
        int k = first[i];
        while(k!=-1){
            printf("%d %d %d
    ",u[k],v[k],w[k]);
            k = next[k];
        }
    }
    

    Bellman—Ford

    ​ Bellman-Ford算法:可以处理带有负权边的图

    ​ 算法思想:对所有边遍历k次,得到的是源点最多经过k条边到达其他路径的最短路径长度。因此总的只需要进行k-1 轮

    ​ 算法核心语句:

    //算法核心语句
    for(int k=1;k<=n-1;++k){
        for(int i=1;i<=m;++i){
            if(dis[v[i]]>dis[u[i]]+w[i])
                dis[v[i]] = dis[u[i]]+w[i];
        }
     }
    

    ​ 示例程序:

    #include<iostream>
    #include<cstdio>
    #define inf 99999
    using namespace std;
    int main(){
        int dis[10],u[10],v[10],w[10];  //u[i]➡v[i]:w[i] 
        int n,m; //n个顶点m条边
        int source;
        cin>>n>>m;
        //读入边
        for(int i=1;i<=m;++i){
            cin>>u[i]>>v[i]>>w[i];
        }
        //初始化dis数组
        for(int i=1;i<=n;++i){
            dis[i] = inf;
        }
        cout<<"请输入源点:"<<endl;
        cin>>source;
        dis[source] = 0;
    
        //算法核心语句
        for(int k=1;k<=n-1;++k){
            for(int i=1;i<=m;++i){
                if(dis[v[i]]>dis[u[i]]+w[i])
                    dis[v[i]] = dis[u[i]]+w[i];
            }
        }
    
        for(int i=1;i<=n;++i){
            printf("%d ",dis[i]);
        }
        return 0;
    }
    
    // 5 5
    // 2 3 2
    // 1 2 -3
    // 1 5 5
    // 4 5 2
    // 3 4 3
    

    ​ 说明:

    1. 判断是否有负权回路:在执行n-1次遍历后,再进行一次遍历,如果有值发生改变,则有负权回路

    2. 大多数时候,再n-1次循环之前就已经得到了最终结果,可以通过引入标志位提前终止循环。

    3. 在每一次实施松弛操作后,会有一些顶点的最短路径的值将不再改变,可以对此进行优化,优化的思路:

      采用队列,每次选取队首顶点u,对顶点u的所有出边进行松弛,例如有一条u➡v的边,如果这条边使得源点 到v的最短路程变短,且顶点v不在队列中,就将v放入队尾。如果顶点v已经在队列中,就不在放入(故需要 一个标志数组加以判断),当对顶点的所有出边松弛完毕后,顶点u出队。循环直到队列为空。

  • 相关阅读:
    12个Web开发者应该掌握的Firebug技巧
    sql语句修改表结构
    从数据库中查询数据
    收发短信API
    日志12.03
    监听短信数据库变化
    漫谈C语言及如何学习C语言(转)
    阅读短信
    在src文件中寻找短信数据库表
    拦截短信示例1
  • 原文地址:https://www.cnblogs.com/ma-liner/p/14196581.html
Copyright © 2020-2023  润新知