最短路径算法

首先是floyd算法,这种算法可以求出图中任意两点之间的最短距离,但是计算的时间复杂度较高,为O(n^3)。具体实现思路如下:

  1. 类似于动态规划,设置动态数组d[k][i][j],代表着在只允许经过前k个点时,点i到点j之间的最短路径。其中d[k][i][i]全为0,然后输入边长可以赋予给d[0][i][j]。
  2. 设置三重循环,对k,i,j进行迭代,每加入一个新的中间点k,那么d[k-1][i][k]+d[k-1][k][j]就是当路径中强行加入点k后的长度,若效果好于前一个最优路径d[k-1][i][j],则d[k][i][j]的最优路径就是它;否则维持上一个最优路径d[k-1][i][j]。
  3. 循环完成,得到任意两点之间的最优路径,输出d[n][1][n]。
    核心代码如下:
for(int k=1;k<=nump;k++){
            for(int i=1;i<=nump;i++){
                for(int j=1;j<=nump;j++){
                    //当允许经过前k-1个点,i到j之间不存在经过点k的路径时
                    if(d[k-1][i][k]==inf || d[k-1][k][j]==inf){
                        d[k][i][j] = d[k-1][i][j];
                    }
                    //当允许经过前k-1个点,i到j之间不存在路径;或者存在路径,但大于加入点k后的新路径时
                    else if((d[k-1][i][j]==inf) || (d[k-1][i][k]+d[k-1][k][j] < d[k-1][i][j])){
                        d[k][i][j] = d[k-1][i][k]+d[k-1][k][j];
                    }
                    //其它情况,例如允许经过前k-1个点,存在路径且小于加入点k后的新路径
                    else{
                        d[k][i][j] = d[k-1][i][j];
                    }
                }
            }
        }

给出例题:
在这里插入图片描述
代码:

/*floyd算法求最短路径*/
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;

#define inf 10000

int nump,nume; //路口数和路数
int d[101][101][101]; //d[k][i][j]代表从i点到j点,中间只允许加入前k个点时的最短路径长度
int main()
{
    memset(d,inf,sizeof(d));  //所有路径初始化

    while(scanf("%d %d",&nump,&nume)!=EOF && nump!=0 && nume!=0){
        //点a到点a距离总为0
        for(int i=1;i<=nump;i++){
            for(int k=0;k<=nump;k++){
                d[k][i][i] = 0;
            }
        }
        //初始化两点之间距离
        for(int i=1;i<=nume;i++){
            int a,b,len;
            scanf("%d %d %d",&a,&b,&len);
            d[0][a][b] = len; d[0][b][a] = len;
        }

        for(int k=1;k<=nump;k++){
            for(int i=1;i<=nump;i++){
                for(int j=1;j<=nump;j++){
                    //当允许经过前k-1个点,i到j之间不存在经过点k的路径时
                    if(d[k-1][i][k]==inf || d[k-1][k][j]==inf){
                        d[k][i][j] = d[k-1][i][j];
                    }
                    //当允许经过前k-1个点,i到j之间不存在路径;或者存在路径,但大于加入点k后的新路径时
                    else if((d[k-1][i][j]==inf) || (d[k-1][i][k]+d[k-1][k][j] < d[k-1][i][j])){
                        d[k][i][j] = d[k-1][i][k]+d[k-1][k][j];
                    }
                    //其它情况,例如允许经过前k-1个点,存在路径且小于加入点k后的新路径
                    else{
                        d[k][i][j] = d[k-1][i][j];
                    }
                }
            }
        }
        cout<<d[nump][1][nump]<<endl;
    }
    return 0;
}
/*
输入示例:
3 3
1 2 5
2 3 5
3 1 2
输出:
2
*/

其次还有dijstra算法求最短路径,该算法可以求出起点到任意点的最短路径,时间复杂度为O(n^2),运算更快。基本思路如下:

  1. 将起点到任一点的最短路径设为d[i],初始化为无穷大;
  2. 利用链表来记录每个点的邻接点,以及与邻接点之间的边长;
  3. 初始设起点为新加入点,同时确定起点到起点的最短路径,并标记起点,(标记的目的在于,说明该点到起点的最小距离彻底确定下来了)
  4. 逐个搜索新加入点的邻接点,计算并判断当前邻接点到起点的最小距离,记录在dis数组中。搜索完成后,寻找当前没有被标记的点中,距离起点距离最小的点,将该点标记,并作为新的加入点;
  5. 循环步骤4(n-1)次,保证每个点都会在某时刻成为新加入点。
  6. 得到起点到任一点的最短路径。

核心代码如下:

int newp = st;  //初始时,先将起点作为新加入点

        //搜寻nump-1轮,能够保证每个点都有会被设为newp的时刻
        for(int i=1;i<nump;i++){
            //从新加入的点开始搜寻,寻找它的邻点到起点的最短距离
            for(int j=0;j<e[newp].size();j++){
                int next = e[newp][j].next;
                int len = e[newp][j].len;
                int co = e[newp][j].cost;
                //dis[next]更新条件:新的dis距离更短,或者距离相同但是花费更少
                if(mark[next] == true) continue;
                if(dis[next]>dis[newp]+len || (dis[next]==dis[newp]+len && cost[next]>cost[newp]+co)){
                    dis[next] = dis[newp]+len;
                    cost[next] = cost[newp]+co;
                }
            }

            //找出当前,除了已经标记确定了的dis[i]之外,其余点中到起点的距离最小的点,
            //这个最短距离的点就是新的加入点,它到起点的最短距离已经确认,被mark数组标记
            int minlen = inf; int minp;
            for(int i=1;i<=nump;i++){
                if(mark[i]==true) continue;
                if(dis[i]<minlen){
                    minlen = dis[i];
                    minp = i;
                }
            }
            mark[minp] = true;
            newp = minp;
        }
        cout<<dis[en]<<" "<<cost[en]<<endl;

例题如下:
在这里插入图片描述
代码:

/*dijstra算法求最短路径*/
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <vector>
using namespace std;

#define inf 100000

int nump,nume;  //点数和边数
int mark[1001]; //已经加入点1到点i的最小路径集合中的点
int dis[1001];  //点1到点i之间的最小距离
int cost[1001];  //点1到点i之间需要的最小费用
int st,en;  //起点和终点

struct edge
{
    int next;  //下一个点
    int len;  //两点连接的边长
    int cost;  //边的花费
};

vector<edge> e[1001];

int main()
{
    //初始化标志数组和距离数组,默认dis和cost都是无穷大
    for(int i=0;i<=1000;i++){
        mark[i] = false;
        dis[i] = inf;
        cost[i] = inf;
    }

    while(scanf("%d %d",&nump,&nume)!=EOF && nump!=0 && nume!=0){
        for(int i=1;i<=nume;i++){
            e[i].clear();
        }
        for(int i=1;i<=nume;i++){
            int a,b,d,p;
            scanf("%d %d %d %d",&a,&b,&d,&p);
            //无向图,点a和点b互相将对方的信息接入自己的邻点集合中
            edge x;
            x.next = b;x.len = d;x.cost = p;e[a].push_back(x);
            x.next = a;e[b].push_back(x);
        }

        scanf("%d %d",&st,&en);
        mark[st] = true;
        dis[st] = 0;
        cost[st] = 0;

        int newp = st;  //初始时,先将起点作为新加入点

        //搜寻nump-1轮,能够保证每个点都有会被设为newp的时刻
        for(int i=1;i<nump;i++){
            //从新加入的点开始搜寻,寻找它的邻点到起点的最短距离
            for(int j=0;j<e[newp].size();j++){
                int next = e[newp][j].next;
                int len = e[newp][j].len;
                int co = e[newp][j].cost;
                //dis[next]更新条件:新的dis距离更短,或者距离相同但是花费更少
                if(mark[next] == true) continue;
                if(dis[next]>dis[newp]+len || (dis[next]==dis[newp]+len && cost[next]>cost[newp]+co)){
                    dis[next] = dis[newp]+len;
                    cost[next] = cost[newp]+co;
                }
            }

            //找出当前,除了已经标记确定了的dis[i]之外,其余点中到起点的距离最小的点,
            //这个最短距离的点就是新的加入点,它到起点的最短距离已经确认,被mark数组标记
            int minlen = inf; int minp;
            for(int i=1;i<=nump;i++){
                if(mark[i]==true) continue;
                if(dis[i]<minlen){
                    minlen = dis[i];
                    minp = i;
                }
            }
            mark[minp] = true;
            newp = minp;
        }
        cout<<dis[en]<<" "<<cost[en]<<endl;
    }
    return 0;
}
/*
输入数据:
3 2
1 2 5 6
2 3 4 5
1 3
输出:
9 11
*/


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值