最短路问题(各种方法整理)附上一个完美模板

2023-11-12

最短路问题(short-path problem),从某点出发到达另一点所经过的路径权值相加最小的一条路径,就是最短路径。

经典的也是最容易掌握的方法Floyd,Dijkstra两种算法。

1.Floyd算法

Floyd算法可以求解的是任意两点的最短路径,功能强大,因此复杂度也很高,但是非常的好懂。

思想很简单,两点的最短路径无非就是直接从一点到另一点,要么就是中间还存在着其他的点。因此只需要暴力枚举中间点,看看存不存在中间路径长度大于直接路径的长度即可。

给出模板(附带记录路径)

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
int G[105][105];
int n,m;//n边m点
int next[105][105];
void init()
{
    cin>>n>>m;
    for(int i=0; i<=n; i++)//初始化
    {
        for(int j=0; j<=n; j++)
        {
            if(i==j)
                G[i][j]=G[j][i]=0;
            else G[i][j]=G[j][i]=inf;
        }
    }
    for(int i=0; i<m; i++)
    {
        int x,y,w;
        cin>>x>>y>>w;
        if(G[x][y]>w)//有些题目坑人给重复路径
            G[x][y]=G[y][x]=w;
    }
}
void printpath(){
    int st=1,ed=n;
    while(st!=ed){
        cout<<st<<"->";
        st=next[st][ed];
    }
    cout<<ed<<endl;
}
void Floyd()
{
    for(int i=1; i<=n; i++)//初始化路径
        for(int j=1; j<=n; j++)
            next[i][j]=j;

    for(int k=1; k<=n; k++)
        for(int i=1; i<=n; i++)
            for(int j=1; j<=n; j++)
                if(G[i][j]>G[i][k]+G[k][j])
                {
                    G[i][j]=G[i][k]+G[k][j];
                    next[i][j]=next[i][k];
                }
    cout<<G[1][n]<<endl;
    printpath();
}

int main()
{
    init();
    Floyd();
    return 0;
}

代码非常的简洁明了。是最容易弄懂的一个算法。但是复杂度偏高,针对有些题目没必要全部算出最短路,只需要算出单点的最短路。因此有时候复杂度会超限。


2.Dijkstra算法

说实话Dijkstra算法有点像最小生成树,也是开一个数组然后不断更新更新。

Dijkstra算法是先纳入起始点到点集合,然后dis[j]记录的是这个点集合到这个j这个点的距离。因此不断的去纳入新的点,去更新现有的距离。

给出一个带记录路径的模板

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
int G[105][105];
int n,m;//n边m点
bool vis[105];
int dis[105],pre[105];
void init()
{
    cin>>n>>m;
    for(int i=0; i<=n; i++)//初始化
    {
        for(int j=0; j<=n; j++)
        {
            if(i==j)
                G[i][j]=G[j][i]=0;
            else G[i][j]=G[j][i]=inf;
        }
    }
    for(int i=0; i<m; i++)
    {
        int x,y,w;
        cin>>x>>y>>w;
        if(G[x][y]>w)//有些题目坑人给重复路径
            G[x][y]=G[y][x]=w;
    }
}

void printpath(int v){
    int p=n,cnt=0;
    int ans[105];
    while(p!=v){
        ans[cnt++]=p;
        p=pre[p];
    }
    cout<<v;
    for(int i=cnt-1;i>=0;i--)
        cout<<"->"<<ans[i];
    cout<<endl;
}

void Dijkstra(int v)
{
    int minnum,next;
    for(int i=0; i<=n; i++)
    {
        dis[i]=G[v][i];
        vis[i]=0;
        if(i!=v&&G[v][i]!=inf)
            pre[i]=v;
        else pre[i]=-1;
    }
    vis[v]=1;
    dis[v]=0;
    for(int i=1; i<n; i++) //每次纳入一个点,需要操作n-1次
    {
        minnum=inf;
        for(int j=1; j<=n; j++)//找出距离最小的点当做下一个操作点
        {
            if(!vis[j]&&minnum>dis[j])
            {
                minnum=dis[j];
                next=j;
            }
        }
        if(minnum==inf)
            break;
        vis[next]=1;
        for(int j=1; j<=n; j++) //更新这个操作点到其他点的距离
        {
            if(!vis[j] && G[next][j]!=inf && dis[next]+G[next][j]<dis[j])
            {
                dis[j]=G[next][j]+dis[next];
                pre[j]=next;
            }
        }
    }
    cout<<dis[n]<<endl;
    printpath(v);
}

int main()
{
    init();
    Dijkstra(1);//起始点到最后个点的距离
    return 0;
}


3.Bellman-Ford算法

从百度上弄来的代码,很直观很好理解,对每条边,跑n-1次,每次松弛(妈的,什么叫松弛!?其实就是判断纳入这条边之后对最短路有没有影响,在具体点就是这行代码

dis[edge[j].v] > dis[edge[j].u] + edge[j].cost
)我是不太喜欢那些被装饰的语句的,能简单就简单,能用代码讲清楚的就别搞七搞八。

另spfa是该算法的队列优化

#include<bits/stdc++.h>
using namespace std;
#define MAX 0x3f3f3f3f
#define N 1010

int nodenum, edgenum, original; //点,边,起点

struct Edge //边
{
    int u, v;
    int cost;
};

Edge edge[N];
int dis[N], pre[N];//dis是起点到这个点的最短路,pre存路径

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\n", 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\n", dis[i]);
            printf("Path:");
            print_path(i);
        }
    else
        printf("have negative circle\n");
    return 0;
}


根据pat考试中L2-001题目整理的完美模板,包含一切

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
int n,m,st,ed;
int num[505],vis[505],dis[505],pre[505],sum[505],cnt[505];
//每个节点的人数。是否走过。最短距离。前驱。最大救援人数数量.方案数
int G[505][505];//图
void path(int i){
    if(pre[i]!=-1){
        path(pre[i]);
        cout<<pre[i]<<" ";
    }
}
int main()
{
    cin>>n>>m>>st>>ed;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            if(i==j)
                G[i][j]=0;
            else G[i][j]=inf;
        }
    }
    for(int i=0;i<n;i++)
        cin>>num[i];
    for(int i=0;i<m;i++){
        int x,y,w;
        cin>>x>>y>>w;
        G[x][y]=G[y][x]=w;
    }
    memset(dis,inf,sizeof(dis));
    memset(vis,0,sizeof(vis));
    memset(cnt,0,sizeof(cnt));
    memset(sum,0,sizeof(sum));
    memset(pre,-1,sizeof(pre));

    dis[st]=0;
    vis[st]=1;
    cnt[st]=1;
    sum[st]=num[st];

    for(int i=0;i<n;i++){
        int minnum=inf,next=st;
        for(int j=0;j<n;j++){
            if(vis[j]==0&&minnum>dis[j]){
                minnum=dis[j];
                next=j;
            }
        }
        vis[next]=1;
        for(int j=0;j<n;j++){
            if(vis[j]==0){
                if(dis[j]>dis[next]+G[next][j]){
                    dis[j]=dis[next]+G[next][j];
                    sum[j]=num[j]+sum[next];
                    cnt[j]=cnt[next];
                    pre[j]=next;
                }
                else if(dis[j]==dis[next]+G[next][j]){
                    cnt[j]=cnt[next]+cnt[j];
                    if(sum[j]<sum[next]+num[j]){
                        sum[j]=sum[next]+num[j];
                        pre[j]=next;
                    }
                }
            }
        }
    }
    cout<<cnt[ed]<<" "<<sum[ed]<<endl;
    path(ed);
    cout<<ed<<endl;
    return 0;
}


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

最短路问题(各种方法整理)附上一个完美模板 的相关文章

  • WebClient.DownloadDataAsync 冻结了我的 UI

    我在 Form 构造函数中的 InitializeComponent 之后有以下代码 using WebClient client new WebClient client DownloadDataCompleted new Downloa
  • 是否可以从 C++ 应用程序调用 C# 应用程序?

    我是一名编程学生 现在我已经上了两门 C 课程 这个学期我将参加我的第一门 C 课程 出于好奇 是否可以从 C 应用程序调用 C 应用程序 如果是的话 是否还可以检查运行该程序的计算机是否具有 NET框架 我只是很好奇 我想如果可能的话 这
  • 无法将 std::min 传递给函数,std::min 的副本有效

    Passing std min函数无法编译 我复制了 libcpp 声明std min进入我的源文件并且它可以工作 std 版本有什么问题 clang 和 gcc 也会发生同样的情况 在 Godbolt 上测试 https godbolt
  • IEnumerable 的 String.Join(string, string[]) 的类似物

    class String包含非常有用的方法 String Join string string 它从数组创建一个字符串 用给定的符号分隔数组的每个元素 但一般来说 它不会在最后一个元素之后添加分隔符 我将它用于 ASP NET 编码 以用
  • C free() 是如何工作的? [复制]

    这个问题在这里已经有答案了 可能的重复 malloc 和 free 如何工作 https stackoverflow com questions 1119134 how malloc and free work include
  • XPATH 查询、HtmlAgilityPack 和提取文本

    我一直在尝试从名为 tim new 的类中提取链接 我也得到了解决方案 给出了解决方案 片段和必要的信息here https stackoverflow com questions 2982862 extracting a table ro
  • 并行化斐波那契序列生成器

    我正在学习并行化 在一项练习中 我得到了一些我应该提高性能的算法 其中之一是斐波那契数列生成器 array 0 0 array 1 1 for q 2 q lt MAX q array q array q 1 array q 2 我怀疑 这
  • MFC CList 支持复制分配吗?

    我在 MSVC 中查找了 CList 定义afxtempl h http www cppdoc com example mfc classdoc MFC AFXTEMPL H html并记录在MSDN http msdn microsoft
  • 将设置函数(setter)标记为 constexpr 的目的是什么? [复制]

    这个问题在这里已经有答案了 我无法理解将 setter 函数标记为的目的constexpr 自 C 14 起这是允许的 我的误解来自以下情况 我使用 constexpr c tor 声明一个类 并且我将通过创建该类的 constexpr 实
  • C 中“complex”的默认类型

    根据我读过的文档 C99 和更高版本的支持float complex double complex and long double complex作为复杂类型 但是 此代码在使用时编译时不会发出警告gcc Wall Wextra inclu
  • 静态类与类的实例

    我有一个静态类 用于访问我的公共属性 整个应用程序的全局属性 和我在应用程序运行期间使用的方法 例如 我在静态类中设置了一些属性 并且在应用程序运行时我可以从属性中获取值 但我可以使用单例模式创建非静态类并以相同的方式使用它 问题 对于我的
  • 如何在win32中使用GetSaveFileName保存文件?

    我编写此代码是为了获取 fileName 来保存我的文件 include stdafx h include
  • 你好,我最近正在开发我的新游戏,我遇到了*无限跳跃*的问题

    所以基本上当我按跳跃 空格键时我会跳跃但是如果我连续按空格键它 只是跳啊跳啊跳等等 我不想要我只想它跳一次 code if Input GetKeyDown space isGrounded velocity y Mathf Sqrt ju
  • 使用 C# 中的 Google 地图 API 和 SSIS 包获取行驶距离

    更新 找到了谷歌距离矩阵并尝试相应地修改我的代码 我在这里收到无效参数错误 return new GeoLocation dstnc uri ToString catch return new GeoLocation 0 0 https 基
  • 时间:2019-03-17 标签:c++fstream并发访问

    如果从不同的进程 线程同时访问文件会发生什么 据我所知 没有锁定文件的标准方法 只有操作系统特定的功能 就我而言 文件将被经常读取而很少写入 现在如果A打开一个文件进行读取 ifstream 并开始读取块 和B打开相同的文件进行写入 ofs
  • ALTER TABLE ... ADD CONSTRAINT 失败时将事务回滚到保存点

    有没有办法在事务中添加检查约束and如果失败回滚到以前的保存点 而不是回滚整个事务 就我而言 当 ALTER TABLE ADD CONSTRAINT 命令失败时 事务无法回滚到保存点 尝试这样做会引发 InvalidOperationEx
  • 使用 xslt 将 xml 转换为 xsl-fo 时动态创建超链接?

    我想使用 xsl 文件在 PDF 报告中创建标题 如果源文件包含超链接 则应将其呈现为超链接 否则呈现为纯文本 例如 我的 xml 如下所示 a href http google com target blank This is the h
  • C 中使用 getrandom 实现随机浮点数

    我试图生成一个介于 0 和 1 之间的随机浮点数 无论是在 0 1 还是 0 1 对我来说都不重要 网上关于此的每个问题似乎都涉及rand 呼叫 播种time NULL 但我希望能够每秒多次调用我的程序 并每次都获得不同的随机数 这引导我找
  • 无法识别解决方案文件夹中的 Visual Studio 2017 Nuget.config

    我在使用 Visual Studio 2017 时遇到问题 新的解决方案不断引用 C Users yopa AppData Roaming NuGet Nuget config 中意外位置的 Nuget config 文件 我已将 nuge
  • 如何提高环复杂度?

    对于具有大量决策语句 包括 if while for 语句 的方法 循环复杂度会很高 那么我们该如何改进呢 我正在处理一个大项目 我应该减少 CC gt 10 的方法的 CC 并且有很多方法都存在这个问题 下面我将列出一些例如我遇到的问题的

随机推荐

  • 【C语言】小游戏-扫雷(清屏+递归展开+标记)

    大家好 我是深鱼 目录 一 游戏介绍 二 文件分装 三 代码实现步骤 1 制作简易游戏菜单 2 初始化棋盘 11 11 3 打印棋盘 9 9 4 布置雷 5 计算 x y 周围8个坐标的和 6 排查雷 lt 1 gt 清屏后打印棋盘 lt
  • Python:赋值,浅拷贝(copy)和深拷贝(deepcopy)

    基础知识请查看之前博客 Python 对象 可变对象与不可变对象 赋值 浅拷贝和深拷贝的关键问题 修改一个变量 会不会导致另外拷贝出来的对象的改变 不可变对象 import copy a1 0 a2 a1 a3 copy copy a1 a
  • 使用https://mail.google.com/登录GMail

    原来使用gmail google com登录 登录可以进去 但查看邮件时 总是出现 Oop unable to reach Gmail Please check your internet connection and try again
  • spring-boot后端解决跨域问题

    代码 import cn hutool log Log import cn hutool log LogFactory import com alibaba fastjson JSONObject import org springfram
  • 添加静态路由实现不同网段的路由的通信和不用网段之间设备的通信

    两不同网段的路由器 如何互通 三个案例详解 gzmenghai com
  • 下一代电信城域网设计原则

    下一代电信城域网设计原则 作者 epon 运营商早期建设的IP城域网多采用大L3 小L3的组网模式 核心层旁挂BAS 在运营中遇到很多问题 过大的二层网络 导致网络的安全性 可靠性较差 网络不可管理 传统L3设备 采用低成本ASIC套片 提
  • error:expected '=',',',';','asm'or'_attribute_'

    今天在Linux上调一个存包队列 当用gcc编译时 出现error expected asm or attribute 等错误 这个错误是出现在两个函数上 这两个函数的返回类型是bool 当我把bool类型改为void 再进行编译时 错误就
  • 菜鸟教程《Python 3 教程》笔记(18):File(文件)方法

    菜鸟教程 Python 3 教程 笔记 18 18 File 文件 方法 18 1 open 方法 18 2 file 对象 18 2 1 flush 18 2 2 fileno 18 2 3 isatty 18 2 4 truncate
  • PROFINET趣谈——设备模型

    设备名 MAC地址和IP地址是为了在网络中找到对应设备 而要定位确切的输入 IX1 1 输出 QW2 就需要熟悉设备模型的概念 PROFINET IO的设备类型与PROFIBUS几乎相同 如图所示 设备模型包括若干槽 slot 与子槽 su
  • Java内存泄露监控工具:JVM监控工具介绍

    jstack 如果java程序崩溃生成core文件 jstack工具可以用来获得core文件的java stack和native stack的信息 从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题 另外 jstack工具还可以附
  • BUAA词频统计(树实现)

    问题描述 编写程序统计一个英文文本文件中每个单词的出现次数 词频统计 并将统计结果按单词字典序输出到屏幕上 要求 程序应用二叉排序树 BST 来存储和统计读入的单词 注 在此单词为仅由字母组成的字符序列 包含大写字母的单词应将大写字母转换为
  • Linux 解决vi键盘方向键出现字母的问题

    修改 etc vim vimrc tiny 1 将 set compatible 兼容模式 改成 set nocompatible 非兼容模式 2 添加 set backspace 2 解决退格键无法使用
  • 【完全开源】小安派-Cam-D 摄像头核心板

    文章目录 一 概述 二 系统框图 三 摄像头电路 四 内存卡电路 五 IO引脚说明 六 资料 一 概述 小安派 Cam D AiPi Cam D 是安信可科技为高性能模组Ai M61 32S设计的一款摄像头核心板 引脚完全兼容Ai WB1
  • MFC :CCoolBar 的替代方案 CDockablePane。

    阅读受众需有一定MFC知识储备 技术支持 http www cnblogs com shuhaoc archive 2011 06 26 cdockableform html 在以往很多使用CCoolBar实现窗口停靠功能 但是在VS201
  • 【C++】Modbus通讯

    C Modbus通讯 2016年06月22日 20 37 48 Taily老段 阅读数 10298 版权声明 本文为博主原创文章 未经博主允许不得转载 如遇到疑问 评论会给出答复 学习交流 关注页面微信公众号 吃良心 拉思想 https b
  • R语言入门教程知识 第七章 特殊值

    以下为本章所用代码 letters letters 5 9 LETTERS LETTERS 6 10 month name month name 7 11 month abb month abb 8 12 pi NA length vec
  • 手撕self-attention代码_从0实现self-attention_附学习路线

    一 前言 科研需要 前几天自学了transformer 在理解self attention时 发现网上并没有一套成熟易懂的学习路线 对新手及其不友好 大多数教程只重视理论和公式的讲解 没有从零开始的代码实战 因此 我在这里整理了一条最适合新
  • python爬虫实战之最简单的网页爬虫教程

    在我们日常上网浏览网页的时候 经常会看到一些好看的图片 我们就希望把这些图片保存下载 或者用户用来做桌面壁纸 或者用来做设计的素材 下面这篇文章就来给大家介绍了关于利用python实现最简单的网页爬虫的相关资料 前言 网络爬虫 又被称为网页
  • 十--nodejs原理(事件循环)

    一 事件循环 什么是事件循环 事件循环使得nodejs可以通过将操作转移到系统内核中来执行非阻塞I O操作 尽管javascripts是单线程的 由于大多数现代内核是多线程的 因此它们可以处理在后台执行的多个操作 当这些操作之一完成时 内核
  • 最短路问题(各种方法整理)附上一个完美模板

    最短路问题 short path problem 从某点出发到达另一点所经过的路径权值相加最小的一条路径 就是最短路径 经典的也是最容易掌握的方法Floyd Dijkstra两种算法 1 Floyd算法 Floyd算法可以求解的是任意两点的