算法板子:最短路问题——包含朴素Dijkstra算法、堆优化版的Dijkstra算法、SPFA算法、Floyd算法

news2024/11/25 6:37:55

目录

    • 1. 几种算法的用途
    • 2. Dijkstra算法——求源点到其他所有点的最短路径(不能处理负边权)
      • (1)朴素Dijkstra算法——适用于稠密图
      • (2)堆优化版的Dijkstra算法——适用于稀疏图
    • 4. SPFA算法——求源点到其他所有点的最短路径、判断是否存在负环(可以处理负边权)
      • (1)求有负边权的图的最短路径——求源点到其他所有点的最短路径
      • (2)判断图中是否存在负环(负环指环的权重和为负数)
    • 5. Floyd算法——求图中任意两点之间的最短路径(可以处理负边权)

1. 几种算法的用途

在这里插入图片描述

在这里插入图片描述

2. Dijkstra算法——求源点到其他所有点的最短路径(不能处理负边权)

(1)朴素Dijkstra算法——适用于稠密图

#include <iostream>
#include <cstring>
using namespace std;

const int N = 500 + 10;
int n, m;

// 这道题边的条数接近于顶点数量的²,边多是稠密图,用邻接矩阵g来存图; 如果一个图有n个节点,那么邻接矩阵的大小就是n*n
int g[N][N];

// d[i]代表源点s到i号点的最短路径; 比如d[4]=3代表源点到点4的最短路径为3
// inf代表int值的无穷大
int d[N], inf = 0x3f3f3f3f;
// vis数组标识某个点是否出圈; vis[4]=0代表节点4没有出圈
int vis[N];


// 传入源点s,计算出各个点到源点s的最短路径
int dijkstra(int s)
{
    // 先将源点到所有点(包括0点)的距离初始化为无穷大inf
    for (int i = 0; i <= n; i ++ ) d[i] = inf;
    // 修改源点到源点的最短路径, 为0
    d[s] = 0;
    
    for (int i = 1; i < n; i ++ )
    {
        int u = 0;
        // u保存圈内离源点最近的点
        for (int j = 1; j <= n; j ++ )
            if (!vis[j] && d[j] < d[u]) u = j;
        // 将找到的最近的点u出圈
        vis[u] = 1;
        // 更新源点到出圈的点u的邻接点的最短路径
        for (int j = 1; j <= n; j ++ )
        {
            // g[u][j]的值不等于inf代表j是u的邻接点; d[j]代表源点s到点j到的最短路径; d[u]+g[u][j]代表通过u这个点到达点j的路径长度
            // 对比一下点j是原先的路径更短还是通过点u到达点j是路径更短, 更新源点到点j的最短路径
            if (g[u][j] != inf) d[j] = min(d[j], d[u] + g[u][j]);
        }
    }
    
    // 如果源点1到点n的最短路径为无穷大, 说明不存在最短路径
    if (d[n] == inf) return -1;
    
    // 如果最短路径不是无穷大, 说明存在最短路径
    return d[n];
}

int main()
{
    cin >> n >> m;
    
    // 将邻接矩阵每条边的权重都初始化为无穷大
    memset(g, 0x3f, sizeof g);
    // 使用邻接矩阵存储图; 若某个点到某个点有边,那么就在对应位置上存上权重,且始终存最小权重
    for (int i = 0; i < m; i ++ )
    {
        int a, b, w;
        cin >> a >> b >> w;
        g[a][b] = min(g[a][b], w); 
    }
    
    // 因为这道题要算点1到点n的最短路径, 那么源点就可以设置成1, 通过dijkstra算法可以得到源点1到所有点的最短路径, 并在算法的最后返回源点1到点n的最短路径
    cout << dijkstra(1) << endl;
        
    return 0;
}

(2)堆优化版的Dijkstra算法——适用于稀疏图

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;

const int N = 2e5 + 10;
typedef pair<int, int> PII;

// 优先级队列q相当于大根堆
// 这里堆顶放的是距离源点最近的点,比如该点距离源点的距离为3,其他点距离源点的距离更大,取相反数后插入堆中,那么距离最近的就是值最大的,放在堆顶
// 堆q每个元素为一个对组,键为源点到该点的最短路径,值是点
priority_queue<PII> q;
// vis标记某点是否已出队,只有未出队的才能更新它的邻接点的最短路径; vis[2]=0代表2这个点未出队
// d[i]代表源点到点i的最短路径
int vis[N], d[N], inf = 0x3f3f3f3f;
// 这道题边比较少,是稀疏图,用邻接表存图; w数组存储权重
int h[N], e[N], ne[N], w[N], idx;

int n, m;

void add(int a, int b, int c)
{
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++ ;
}

int dijkstra(int s)
{
    // 将源点到所有点的最短路径初始化为无穷大
    for (int i = 0; i <= n; i ++ ) d[i] = inf;
   
    // 将源点加入堆
    d[s] = 0; q.push({-d[s], s});
    
    while (q.size())
    {
        // 取堆顶; 取出距离源点最近的点
        auto t = q.top(); q.pop();
        int u = t.second;
        // 如果已经出队过,就不能更新邻接点
        if (vis[u]) continue;
        // 如果没有出队过,标记它现在已经出队过
        vis[u] = 1;
        // 更新该点的邻接点的最短路径
        for (int i = h[u]; i != -1; i = ne[i])
        {
            int j = e[i], c = w[i];
            // 如果邻接点需要更新,则加入堆中
            if (d[u] + c < d[j])
            {
                d[j] = d[u] + c;
                q.push({-d[j], j});
            }
        }
    }
    
    if (d[n] == inf) return -1;
    
    return d[n];
}

int main()
{
    cin >> n >> m;
    memset(h, -1, sizeof h);
    for (int i = 0; i < m; i ++ )
    {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
    }
    
    cout << dijkstra(1) << endl;
    
    return 0;
}

4. SPFA算法——求源点到其他所有点的最短路径、判断是否存在负环(可以处理负边权)

(1)求有负边权的图的最短路径——求源点到其他所有点的最短路径

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;

const int N = 1e5 + 10;

int h[N], e[N], ne[N], w[N], idx;
queue<int> q;
int d[N], inf = 0x3f3f3f3f;
// vis[i]代表点i是否在队中; vis[3]=1代表点3在队中, vis[3]=0代表点3不在队中
int vis[N];

int n, m;

void add(int a, int b, int c)
{
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++ ;
}

void spfa(int s)
{
    // 将源点到全部点的最短路径全部初始化为无穷大
    memset(d, inf, sizeof d);
    
    d[s] = 0;
    // 将源点入队
    q.push(s); vis[s] = 1;
    
    while (q.size())
    {
        // 出队队头,标记队头不在队中
        int f = q.front(); q.pop(); vis[f] = 0;
        // 更新队头邻接点的最短路径
        for (int i = h[f]; i != -1; i = ne[i])
        {
            int j = e[i], c = w[i];
            if (d[f] + c < d[j])
            {
                d[j] = d[f] + c;
                // 如果点j不在队中,则加入队中
                if (!vis[j]) q.push(j), vis[j] = 1;
            }
        }
    }
    
    if (d[n] == inf) cout << "impossible" << endl;
    else cout << d[n] << endl;
}

int main()
{
    cin >> n >> m;
    
    memset(h, -1, sizeof h);
    for (int i = 0; i < m; i ++ ) 
    {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
    }
    
    spfa(1);
    
    return 0;
}

(2)判断图中是否存在负环(负环指环的权重和为负数)

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;

const int N = 1e5 + 10;
int n, m;

int h[N], e[N], ne[N], w[N], idx;
queue<int> q;
// 在图上增加一个虚拟源点,虚拟源点到图中每个点都有条权重为0的边
// cnt数组代表虚拟源点到该点的最短路径的边数; 比如cnt[3]=3代表虚拟源点到点3的最短路径的边数为3
int d[N], vis[N], cnt[N];

void add(int a, int b, int c)
{
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++ ;
}

bool spfa()
{
    // 把所有点加入队列中
    for (int i = 1; i <= n; i ++ ) q.push(i), vis[i] = true;
    
    while (q.size())
    {
        int f = q.front(); q.pop(); vis[f] = 0;
        for (int i = h[f]; i != -1; i = ne[i])
        {
            int j = e[i], c = w[i];
            // 更新源点到点j的最短路径
            if (d[f] + c < d[j])
            {
                // 更新最短路径的长度
                d[j] = d[f] + c;
                // 更新最短路径的边数
                cnt[j] = cnt[f] + 1;
                // 如果虚拟源点到点j的边数大于等于节点数,则存在负环
                if (cnt[j] >= n) return true;
                if (!vis[j]) q.push(j), vis[j] = 1;
            }
        }
    }
    
    // 图中不存在负环
    return false;
}

int main()
{
    cin >> n >> m;
    
    memset(h, -1, sizeof h);
    for (int i = 0; i < m; i ++ ) 
    {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
    }
    
    cout << (spfa() ? "Yes" : "No") << endl;
    
    return 0;
}

5. Floyd算法——求图中任意两点之间的最短路径(可以处理负边权)

#include <iostream>
using namespace std;

const int N = 200 + 10;

// d[i][j]代表点i到点j经过节点1~k的最短路径的长度; 例如d[1][3]=2代表点1到点3的最短路径长度为2
int d[N][N], INF = 0x3f3f3f3f;

int n, m, k;

// 更新两点之间的最短路径
void floyd()
{
    for (int k = 1; k <= n; k ++ )
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= n; j ++ )
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}

int main()
{
    cin >> n >> m >> k;
    
    // 处理自环; 图中有可能存在自环,比如点1到点1存在条边,那么点1到点1的最短路径就是0
    for (int i = 1; i <= n; i ++ )
    {
        for (int j = 1; j <= n; j ++ )
        {
            if (i == j) d[i][j] = 0;
            else d[i][j] = INF;
        }
    }
    
    // d可以先看成一个邻接矩阵,存储两点之间边的最小权重
    for (int i = 0; i < m; i ++ )
    {
        int a, b, w;
        cin >> a >> b >> w;
        // 图中两点之间可能有重边,保留权重最小的那条边即可
        d[a][b] = min(d[a][b], w);
    }
    
    floyd();
    
    while (k -- )
    {
        int a, b;
        cin >> a >> b;
        // 如果点a到点b的距离不是无穷大,则存在最短路径; 是无穷大,不存在最短路径; 
        // 这里将无穷大定义为INF/2是因为,就算点a到点b不存在最短路径,但由于存在负边权,有可能使得路径从无穷大变得比无穷大小一些
        // 所以如果当前点a到点b的路径比无穷大的一半还大,我们就认为两点之间还是无穷大,还是没有最短路径
        if (d[a][b] > INF / 2) cout << "impossible" << endl;
        else cout << d[a][b] << endl;
    }
        
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1986174.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

孤独行走的视频素材去哪里找?孤独的人的视频素材哪里找啊

在视频创作和情感表达的项目中&#xff0c;选择合适的孤独行走视频素材尤为重要&#xff0c;因为这种素材能够有效传递人物的情绪状态&#xff0c;增强作品的情感层次。下面将介绍几个优质的视频素材平台&#xff0c;以帮助您更方便地寻找到适合表达孤独与深思的高质量视频素材…

C#基础——泛型

泛型 C# 中的泛型是一种强大的编程特性&#xff0c;它允许你编写类型安全且灵活的代码。泛型允许你定义类、结构体、接口、方法和委托&#xff0c;而不必在编译时指定具体的数据类型。相反&#xff0c;你可以使用类型参数来定义泛型类型或方法&#xff0c;然后在使用时指定具体…

免费的SD-WAN服务

SD-WAN&#xff0c;SASE&#xff0c;零信任是近年来比较火的概念&#xff0c;SD-WAN发展已经很久了&#xff0c;但是真正能够自主研发做SD-WAN的企业其实并不算太多。 比扬云的SD-WAN产品是自主研发的&#xff0c;可控性强&#xff0c;最重要的是具有免费版本&#xff0c;可以免…

C#使用NPOI进行Excel和Word文件处理(二)

文章目录 使用NPOI在C#中处理Excel和Word文件1. 什么是NPOI&#xff1f;2. 安装NPOI2.1 VisualStudio2019引入NPOI 3. 处理Excel文件读取Excel文件写入Excel文件 4. 处理Word文件读取Word文件写入Word文件 总结Github 地址链接参考例子 使用NPOI在C#中处理Excel和Word文件 在C…

软件生命周期(二)

1. 软件生命周期定义 软件生命周期&#xff08;SDLC&#xff09;是软件开始研制到最终废弃不用所经历的各个阶段 – 软件开发模型 2. 瀑布型生命周期模型 瀑布模型规定自上而下&#xff0c;相互衔接的固定次序&#xff0c;如同瀑布流水&#xff0c;逐级下落&#xff0c;具有…

探索全光网技术 | 全光网络技术方案选型建议三(医院场景)

目录 一、场景设计需求二、医院场景拓扑三、部署方式四、产品相关规格说明五、方案优势与特点 注&#xff1a;本文章参考资料为&#xff1a;华三官方资料 - “新华三全光网络3.0解决方案&#xff08;教育&#xff09;”与 锐捷官方资料 - “【锐捷】高校极简以太全光3.X方案设计…

c++ + linux+cmake编译动态库+so调用

步骤如下&#xff1a; 1. 创建动态库2. 编译动态库3. 使用动态库4. 编译程序并链接动态库5. 运行程序 1. 创建动态库 // hello.cpp #include <iostream> #include <string> using namespace std; int hello(string a) {cout << "hello "<< …

树状数组基础知识以及相关习题

文章目录 什么是树状数组&#xff1f;如何理解树状数组如何理解精髓lowbit二叉树和树状数组的结构树状数组的优点树状数组模板单点修改&#xff0c;区间查询区间修改&#xff0c;单点查询区间修改&#xff0c;区间查询树状数组法线段树法 树状数组基础练习题逆序对动态求连续区…

C语言进阶版第六课—操作符

文章目录 1. 操作符的分类2. 原码、反码、补码3. 移位操作符4. 位操作符4.1 位操作符详解4.2 练习4.2.1 不能创建临时变量&#xff0c;实现两个整数的交换4.2.2 求一个整数存储在内存中的二进制1的个数4.2.3 判断一个数是不是2的次方数4.2.4 将二进制第n位置0或置1 5. 逗号表达…

git拉代码

第一步 第二步 第三步

Mysql原理与调优-InnoDB页结构

目录 1.绪论 2.Mysql的数据页组成 2.1 基本组成 2.1 文件头 2.2 页头部&#xff08;Page Header&#xff09; 3.3 infimum supremum 3.4 用户记录 和 空闲空间 3.4.1 用户插入数据步骤 3.4.2 删除数据 3.5 页目录 3.5.1 插入时维护槽的步骤 3.5.2 查询元素的步骤 …

为之“毫厘”、追之“千里”,“仅退款”的存废之争

文丨郭梦仪 橘子&#xff08;化名&#xff09;看到了待审核的起诉书&#xff0c;气不打一处来。这次白嫖行为橘子不打算再忍了。 橘子是拼多多商家&#xff0c;经营化妆品公司&#xff0c;5月份&#xff0c;一个顾客收到货4天说用了过敏&#xff0c;直接退款没有退回产品。橘…

Project Euler_Problem 587_Concave Triangle (背包问题)

原问题&#xff1a; 代码&#xff1a; void solve() {ll i, j, k, p, q, u, v, l, r, x, y, z, z1;z 0;double a, b, c;N 1e9;n 250250;p 1e16;z 250;for (i 1; i < n; i) {C[i] M.NT.prime_pow(i, i, z);}A[0] 1;for (i 1; i < n; i) { x C[i];for (j …

潮流时尚,音质在线的悠律ringbuds pro开放式耳机体验评测

选购耳机时你最看重的要素是什么&#xff1f;对笔者来说&#xff0c;“音质”绝对排在了第一位。然而&#xff0c;当前市场上多数开放式蓝牙耳机音质和舒适性欠佳&#xff0c;让人难以抉择。最近悠律声学品牌推出全新的 OWS 蓝牙耳机ringbuds pro&#xff0c;它以卓越音质、时尚…

streamlit (python构建web)之环境搭建

目录 前言 1. 什么是Streamlit&#xff1f; 2. Streamlit的原理 2.1 demo1-运行计时程序 2.2 demo2-随即密码生成器 3. 安装Streamlit 3.1 安装方式一 3.2 安装方式二 3.2.1 安装 conda 3.2.2 新建一个 conda 环境 3.2.3 激活 conda 环境 3.2.4 安装 Streamlit 库 …

微信小程序教程011-2:京西购物商城实战之TabBar实现

2、tabBar 2.0 创建tabBar分支 运行如下命令,基于master分支,创建本地tabBar子分支,用来开发和tabBar相关的功能 git checkout -b tabbar2.1 创建tabBar页面 在pages目录中,创建首页(home)、分类(cate)、购物车(cart)、我的(my)这4个tabBar页面,在HBuilderX中…

PADS文件与AD文件如何相互转换?

大家好&#xff0c;我是山羊君Goat。 在电子硬件设计中&#xff0c;常常用用到3款EDA设计软件&#xff1a;Altium Designer, Pads, cadence。 Altium Designer: 在高校中使用非常普遍&#xff08;相信很多硬件工程师接触的第一款设计软件就是Altium Designer&#xff09;&…

Python 用户输入和while循环

1、input()函数的工作原理&#xff08;用户输入&#xff09; input()函数让程序暂停运行&#xff0c;等待用户输入一些文本。获取用户输入后&#xff0c;Python将其赋给变量&#xff0c;以其使用。 在input()函数接受一个参数&#xff0c;既要向用户显示的提示&#xff0c;为了…

Flink 实时数仓(八)【DWS 层搭建(二)流量域、用户域、交易域搭建】

前言 今天的任务是完成流量域最后一个需求、用户域的两个需求以及交易域的部分需求&#xff1b; 1、流量域页面浏览各窗口汇总表 任务&#xff1a;从 Kafka 页面日志主题读取数据&#xff0c;统计当日的首页和商品详情页独立访客数。 注意&#xff1a;一般我们谈到访客&…

广东省造林绿化施工丙级资质2024年9月开通申报

关于广东省造林绿化施工丙级资质2024年9月的申报情况&#xff0c;可以归纳如下&#xff1a; 一、申报时间 具体时间&#xff1a;2024年9月1日至9月30日。在此期间&#xff0c;相关企业可以在网上提交申请。 二、申报条件 资历和信誉 1、独立企事业法人资格&#xff1a;申请…