• 《啊哈!算法》第6章最短路径


    第一节 Floyd-Warshall算法

    本算法可以求任意两个点之间的最短路径,又称“多源最短路径”,其时间复杂度为O(n^3)

    其核心部分只有下面几行,注意加法的溢出处理

        //floyd最短路径算法的核心部分
        for(int k = 0; k < n; ++ k){
            for(int i = 0 ; i < n ; ++ i){
                for(int j = 0 ; j < n ; ++ j){
                    if(grid[i][k]!=INT_MAX && grid[k][j]!=INT_MAX &&grid[i][j] > grid[i][k]+grid[k][j]){
                        grid[i][j] = grid[i][k] + grid[k][j];
                        path[i][j] = k;
                    }
                }
            }
        }
    /*
     *基于有向图的floyd最短路径算法
     *floyd算法不能解决带有“负权回路”的图
     */
    
    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <climits>
    #include <cstdio>
    using namespace std;
    
    typedef vector<vector<int> > VVI;
    
    void floyd(VVI& grid, VVI& path){
        int n = grid.size();
        //floyd最短路径算法的核心部分
        for(int k = 0; k < n; ++ k){
            for(int i = 0 ; i < n ; ++ i){
                for(int j = 0 ; j < n ; ++ j){
                    if(grid[i][k]!=INT_MAX && grid[k][j]!=INT_MAX &&grid[i][j] > grid[i][k]+grid[k][j]){
                        grid[i][j] = grid[i][k] + grid[k][j];
                        path[i][j] = k;
                    }
                }
            }
        }
    }
    
    void print(int x, int y, VVI& path){
       int k = path[x][y];
       //cout<<"k="<<k<<endl;
       if(k==-1) return;
       print(x,k,path);
       printf("%d->",k);
       print(k,y,path);
    }
    
    int main(){
        int n,m;
        cin >> n >> m;
        VVI grid(n,vector<int>(m,INT_MAX));
        for(int i = 0 ; i < n; ++ i) grid[i][i] = 0;
        for(int i = 0 ; i < m; ++ i){
            int a,b,e;
            cin >> a >> b >> e;
            grid[--a][--b] = e;
        }
        VVI path(n,vector<int>(m,-1));
        floyd(grid,path);
        //输出最终的结果
        cout<<string(30,'=')<<endl;
        printf("start	end	length	path
    ");
        for(int i = 0 ; i < n ; ++ i){
            for(int j = 0 ;  j < n; ++ j){
                printf("%4d	%4d	%4d	",i+1,j+1,grid[i][j]);
                printf("%d->",i+1);
                print(i,j,path);
                printf("%d
    ",j+1);
            }
            //printf("
    ");
        }
        cout<<string(30,'=')<<endl;
        return 0;
    }
    floyd最短路径算法

     第二节 Dijkstra算法-通过边实现松弛

    /*
     *单源最短路径算法
     */
    
    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <climits>
    using namespace std;
    
    typedef vector<vector<int> > VVI;
    typedef vector<bool> VB;
    typedef vector<int> VI;
    
    void printPath(VI& path, int u, int v){
        vector<int> res;
        res.push_back(v+1);
        while(path[v]!=u){
            res.push_back(path[v]+1);
            v = path[v];
        }
        res.push_back(u+1);
        reverse(res.begin(),res.end());
        cout<<"路径:";
        for(int i = 0 ; i < res.size(); ++ i)
            cout << "->"<< res[i];
        cout<<endl;
    }
    
    int main(){
        int n,m;
        cin >> n >>m;
        VVI grid(n,vector<int>(n,INT_MAX));
        for(int i = 0 ; i < n ; ++ i) grid[i][i] = 0;
        for(int i = 0 ; i < m; ++ i){
            int a,b,e;
            cin >> a >> b >> e;
            --a;--b;
            grid[a][b] = e;
        }
    
        VB visit(n,false);
        visit[0] = true;
    
        VI dist(grid[0].begin(),grid[0].end());
        VI path(n,0);
        for(int i = 0 ; i < n ; ++ i) {
            if(dist[i] == INT_MAX) path[i] = -1;
            else path[i] = 0;
        }
        //Dijkstra算法核心语句
        for(int i = 0 ; i < n-1; ++ i){
            int minDist = INT_MAX, minIndex = i;
            for(int j = 0 ; j < n ; ++ j){
                if(!visit[j] && dist[j] < minDist){
                    minDist = dist[j];
                    minIndex = j;
                }
            }
            visit[minIndex] = true;
            for(int p = 0; p < n;++ p){
                if(grid[minIndex][p] < INT_MAX ){
                    if(dist[p] > dist[minIndex]+grid[minIndex][p]){
                        dist[p] = dist[minIndex]  + grid[minIndex][p];
                        path[p] = minIndex;
                    }
                }
            }
        }
    
        printPath(path,0,n-1);
    
        //输出最终的结果
        cout<<"距离:";
        for(int i = 0 ; i < n; ++ i)
            cout<<"->"<<dist[i];
        cout<<endl;
    
        return 0;
    }
    Dijkstra最短路径算法

     第三节 Bellman-Ford-解决负权边

    Dijkstra算法不能解决带有负权边的图,而Bellman-Ford能计算

    注意松弛操作只需要进行n-1轮即可,因为在一个含有n个顶点的图中,任意两点之间的最短路径最多包含n-1边

    #include <iostream>
    #include <vector>
    #include <algorithm>
    
    #define INF 100000
    
    using namespace std;
    
    struct edge{
        int u;
        int v;
        int w;
        edge(int uu = 0, int vv = 0 , int ww = 0):u(uu),v(vv),w(ww){}
    };
    
    int main(){
        int n,m;
        cin >> n >> m;
        vector<edge> e(m);
        for(int i = 0 ; i < m; ++ i){
            cin >>e[i].u >> e[i].v >> e[i].w;
            --e[i].u;--e[i].v;
        }
        vector<int> dist(n,INF),bak(n,INF);
        dist[0] = 0;
        for(int i = 0; i <n-1; ++ i){
            for(int j = 0; j < n; ++ j) bak[j] = dist[j];
            for(int j = 0; j < m; ++ j){
                if(dist[e[j].v] > dist[e[j].u] + e[j].w){
                    dist[e[j].v] = dist[e[j].u] + e[j].w;
                }
            }
    
            //检测数组是否有更新,如果没有更新,提前退出循环
            bool check = false;
            for(int j = 0; j < n ; ++ j) 
                if(bak[j]!=dist[j]){check=true;break;}
            if(!check) break;
        }
    
        //检测负权回路
        bool flag = false;
        for(int i = 0 ; i < m ; ++ i){
            if(dist[e[i].v] > dist[e[i].u] + e[i].w){
                flag = true;
                break;
            }
        }
        if(flag) cout<<"此图含有负权回路"<<endl;
    
        for(int i = 0 ; i < n; ++ i){
            cout<<dist[i]<<" ";
        }
        cout<<endl;
    
    }
    Bellman-Ford算法
  • 相关阅读:
    RBAC权限管理模型 产品经理 设计
    Redisson 分布式锁
    Jenkins下载历史Build版本的归档文件
    Java JPA @Transient 在Hibernate中应用
    Solving the Top ERP and CRM Metadata Challenges with erwin & Silwood
    MySQL 字符串 分割 多列
    MySQL CONCAT opposite
    Web并发页面访问量统计实现
    UNIX网络编程读书笔记:基本UDP套接口编程
    UNIX网络编程调试工具:tcpdump、netstat和lsof
  • 原文地址:https://www.cnblogs.com/xiongqiangcs/p/3805689.html
Copyright © 2020-2023  润新知