最短路径

最短路径

1.Dijkstra算法

主要解决单源最短路径问题:


    void Dijkstra(int s){
        for(int i = 0;i < maxn;i++)
            dis[i] = INF;

        dis[s] = 0;
        for(int i = 0;i < n;i++){
            int u = -1,min = INF;
            for(int j = 0;j < n;j++){
                if(!visit[j] && dis[j] < INF){
                    u = j;
                    min = dis[j];
                }
            }

            if(u == -1)  return;
            visit[u] = true;
            for(int j = 0;j < n;j++){
                if(!visit[j] && G[u][j] != INF){
                    if(dis[j] > dis[u] + G[u][j]){
                        dis[j] = dis[u] + G[u][j];
                    }
                }
            }
        }
    }

当有两种或以上的最短路径时,一般情况下题目就会给出一个第二标尺,常见如下:

  1. 新增边权,以新增的边权代表花费为例,用cost[u][v]表示 U -> V 的花费,并新增一个数组c[],令从起点s到达u的最小花费为c[u],初始化时只有c[s]为0、其余均为INF。

    for(int v = 0;v < n;v++){
            if(visit[v] == false && G[u][v] != INF){
                if(d[u] + G[u][v] < d[u]){
                    d[v] = d[u] + G[u][v];
                    cost[v] = c[u] + cost[u][v];
                }
                else if(d[u] + G[u][v] == d[u] && c[u] + cost[u][v] < c[v]){
                    c[v] = c[u] + cost[u][v];
               }
        }
    }
  1. 新增点权,以新增的点权代表城市中能收集到的物资为例,用weight[u]表示城市 u 中的物资数目,并新增一个数组w[],令从起点s到达u的能收集的最大物资为w[u],初始化时只有w[s] = weight[s],其余均为0 。

    for(int v = 0;v < n;v++){
        if(visit[v] == false && G[u][v] != INF){
            if(d[u] + G[u][v] < d[u]){
                d[v] = d[u] + G[u][v];
                w[v] = w[u] + weight[v];
            }
            else if(d[u] + G[u][v] == d[u] && c[u] + cost[u][v] < c[v]){
                w[v] = w[u] + weight[v];
           }
        }
    }   
  1. 求最短路径条数,只需要增加一个数组num[],令起点s到达顶点u的最短路径条数为num[u],这样初始化时只有num[s] = 1、其余 num[s] 均为0 。

    for(int j = 0;j < n;j++){
        if(visit[j] == false && G[u][j] != INF){
            if(dis[j] > dis[u] + G[u][j]){      //如果能找到更短的路径就更新 dis 数组
                dis[j] = dis[u] + G[u][j];           
                num[j] = num[u];
            }else if(dis[j] == dis[u] + G[u][j]){   //找到一条长度相等的路径
                num[j] += num[u];                   
            }
        }
    }

2.Bellman-Ford算法

  Dijkstra 算法可以很好的解决无负权图的最短路径问题,但如果出现了负边权,Dijkstra就会失效,为了更好地求解有负权的最短路径问题,需要使用Bellman-Ford算法,Bellman-Ford算法可以解决单源最短路径问题,但也能处理有负权边的情况。


    #include <iostream>
    #include <vector>
    using namespace std;
    const int maxn = 500 + 5;
    const int INF = 1000000000;
    int dis[maxn];
    struct node{
        int v,d;//邻结点和邻接边的边权
        node(int v1,int d1):v(v1),d(d1) {}
    };
    int n,m;
    vector <node> Adj[maxn];
    bool Ford(int s){
        for(int i = 0;i < maxn;i++)
            dis[i] = INF;
        dis[s] = 0;

        for(int i = 0;i < n-1;i++){
            for(int u = 0;u < n;u++)
                for(int j = 0;j < Adj[u].size();j++){
                    int v = Adj[u][j].v;//邻接边的顶点
                    int d = Adj[u][j].d;//邻接边的边权
                    if(dis[u] + d < dis[v]){
                        dis[v] = dis[u] + d;
                    }
            }
        }
            //以下为判断负环的代码
            for(int u = 0;u < n;u++)
                for(int j = 0;j < Adj[u].size();j++){
                    int v = Adj[u][j].v;
                    int d = Adj[u][j].d;
                    if(dis[u] + d < dis[v]){//如果仍可以被松弛
                        return false;           //说明图中有从源点可达的负环
                    }
            }

        return true;
    }
    int main(void){
        int v1,v2,s,d;
        cin>>n>>m>>s;

        cin>>v1>>v2>>d;                 //负权值是单向的
        Adj[v1].push_back(node(v2,d));

        for(int i = 0;i < m-1;i++){
            cin>>v1>>v2>>d;
            Adj[v1].push_back(node(v2,d));
            Adj[v2].push_back(node(v1,d));
        }
        if(Ford(s)){
            cout<<"该图中没有负环。"<<endl;
        }else{
            cout<<"该图中存在负环。"<<endl;
        }
        for(int i = 0;i < n;i++)
            cout<<dis[i]<<" ";
        cout<<endl;
        return 0;
    }
    /*
    3 3 0
    0 1 -3
    0 2 1
    1 2 5

    3 3 0
    0 1 -3
    0 2 1
    1 2 2

    3 3 0
    0 1 -3
    0 2 1
    1 2 1
    */

3.SPFA算法

  SPFA算法是Bellman-Ford算法优化的结果


    vector <node> Adj[maxn];
    bool SPFA(int s){
        for(int i = 0;i < maxn;i++)
            dis[i] = INF;
        dis[s] = 0;

        memset(num,0,sizeof(num);
        queue <int> q;
        q.push(s);

        visit[s] = true;
        num[s]++;
        while(!q.empty()){
            int u = q.front();
            q.pop();
            visit[u] = false;       //不在队列中

            for(int i = 0;i < Adj[u].size();i++){
                int v = Adj[u][i].v;//邻接边的顶点
                int d = Adj[u][i].d;//邻接边的边权
                //松弛操作
                if(dis[v] > dis[u] + d){
                    dis[v] = dis[u] + d;
                    if(!visit[v]){
                        q.push(v);
                        num[v]++;//当前结点入队次数加一
                        visit[v] = true;
                        if(num[v] >= n) return false;       //有可达的负环
                    }
                }
            }
        }
        return true;

    }

4.Floyd算法

  Floyd 算法用来解决全源最短路径问题,即对给定的图G(V,E),求任意两点u,v之间的最短路径长度。


    void Floyd(){
        for(int k = 0;k < n;k++)        //辅助顶点 k (一定要放在最外层)
            for(int i = 0;i < n;i++)
                for(int j = 0;j < n;j++){
                    if(dis[i][k] != INF && dis[k][j] != INF){
                        if(dis[i][j] > dis[i][k] + dis[k][j]){
                            dis[i][j] = dis[i][k] + dis[k][j];
                        }
                    }
                }
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值