• 关于 Bellman-Ford 与 Floyd 算法的一点感想


    在四种常用的最短路算法 Dijkstra, SPFA, floyd, Bellman-Ford 中, Dijks 和 SPFA 的使用较为普遍, 对大多数人来说, 也较为熟悉. 然而, floyd 与 BF 算法在一些特定的情况下也是非常管用的, 因此有必要在这里作出一点总结.
    Floyd的基本思路就是枚举任意两个点i, j, 再枚举任意的第三个点k, 用d[i][k] + d[j][k] 来松弛d[i][j]的值. 时间复杂度为O(n ^ 3), 优点在于可以求出任意两点之间的距离, 在稠密图中也非常管用.

    #include<iostream>
    #include<vector>
    using namespace std;
    const int &INF=100000000;
    void floyd(vector<vector<int> > &distmap,//可被更新的邻接矩阵,更新后不能确定原有边
               vector<vector<int> > &path)//路径上到达该点的中转点
    //福利:这个函数没有用除INF外的任何全局量,可以直接复制!
    {
        const int &NODE=distmap.size();//用邻接矩阵的大小传递顶点个数,减少参数传递
        path.assign(NODE,vector<int>(NODE,-1));//初始化路径数组 
        for(int k=1; k!=NODE; ++k)//对于每一个中转点
            for(int i=0; i!=NODE; ++i)//枚举源点
                for(int j=0; j!=NODE; ++j)//枚举终点
                    if(distmap[i][j]>distmap[i][k]+distmap[k][j])//不满足三角不等式
                    {
                        distmap[i][j]=distmap[i][k]+distmap[k][j];//更新
                        path[i][j]=k;//记录路径
                    }
    }
    void print(const int &beg,const int &end,
               const vector<vector<int> > &path)//传引用,避免拷贝,不占用内存空间
               //也可以用栈结构先进后出的特性来代替函数递归 
    {
        if(path[beg][end]>=0)
        {
            print(beg,path[beg][end],path);
            print(path[beg][end],end,path);
        }
        else cout<<"->"<<end;
    }
    int main()
    {
        int n_num,e_num,beg,end;//含义见下
        cout<<"(不处理负权回路)输入点数、边数:";
        cin>>n_num>>e_num;
        vector<vector<int> > path,
              distmap(n_num,vector<int>(n_num,INF));//默认初始化邻接矩阵
        for(int i=0,p,q; i!=e_num; ++i)
        {
            cout<<"输入第"<<i+1<<"条边的起点、终点、长度(100000000代表无穷大,不联通):";
            cin>>p>>q;
            cin>>distmap[p][q];
        }
        floyd(distmap,path);
        cout<<"计算完毕,可以开始查询,请输入出发点和终点:";
        cin>>beg>>end;
        cout<<"最短距离为"<<distmap[beg][end]<<",打印路径:"<<beg;
        print(beg,end,path);
    }

    Bellman-Ford是SPFA算法的前身, 时间复杂度为O(VE)…非常慢, 但是可以用于判断负权回路. 常用于差分约束系统中. Bellman-Ford这种写法相当暴力, 直接循环nodeNum次, 每次枚举每一条边, 假如这条边可以用于松弛源点到端点的距离, 则进行松弛. 至于判断负环, 再枚举一遍所有边, 假如存在边仍能用于松弛, 则说明存在负权回路.

    #include<iostream>  
    #include<cstdio>  
    using namespace std;  
    
    #define MAX 0x3f3f3f3f  
    #define N 1010  
    int nodenum, edgenum, original; //点,边,起点  
    
    typedef struct Edge //边  
    {  
        int u, v;  
        int cost;  
    }Edge;  
    
    Edge edge[N];  
    int dis[N], pre[N];  
    
    bool Bellman_Ford()  
    {  
        for(int i = 1; i <= nodenum; ++i) //初始化  
            dis[i] = (i == original ? 0 : MAX);  
        for(int i = 1; i <= nodenum - 1; ++i)  
            for(int j = 1; j <= edgenum; ++j)  
                if(dis[edge[j].v] > dis[edge[j].u] + edge[j].cost) //松弛(顺序一定不能反~)  
                {  
                    dis[edge[j].v] = dis[edge[j].u] + edge[j].cost;  
                    pre[edge[j].v] = edge[j].u;  
                }  
                bool flag = 1; //判断是否含有负权回路  
                for(int i = 1; i <= edgenum; ++i)  
                    if(dis[edge[i].v] > dis[edge[i].u] + edge[i].cost)  
                    {  
                        flag = 0;  
                        break;  
                    }  
                    return flag;  
    }  
    
    void print_path(int root) //打印最短路的路径(反向)  
    {  
        while(root != pre[root]) //前驱  
        {  
            printf("%d-->", root);  
            root = pre[root];  
        }  
        if(root == pre[root])  
            printf("%d
    ", root);  
    }  
    
    int main()  
    {  
        scanf("%d%d%d", &nodenum, &edgenum, &original);  
        pre[original] = original;  
        for(int i = 1; i <= edgenum; ++i)  
        {  
            scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].cost);  
        }  
        if(Bellman_Ford())  
            for(int i = 1; i <= nodenum; ++i) //每个点最短路  
            {  
                printf("%d
    ", dis[i]);  
                printf("Path:");  
                print_path(i);  
            }  
        else  
            printf("have negative circle
    ");  
        return 0;  
    }  
    
  • 相关阅读:
    EMQ ---payload
    EMQ --集成搭建
    chome 离线安装包地址
    EMQ ---问题集
    EMQ学习---客户链接资源消耗
    EMQ学习 ---集群
    EMQ 学习---MQTT消息QoS
    EMQ 学习---订阅$SYS主题,捕获客户端上下线消息
    EMQ学习笔记---Clean Session和Retained Message
    elasticsearch学习网站
  • 原文地址:https://www.cnblogs.com/ZeonfaiHo/p/6402844.html
Copyright © 2020-2023  润新知