从1号点到n号点最多经过k条边的最短距离

目录

    • 解析
    • 方法思路
    • 代码解释
    • 代码逐行注释
        • 1. 头文件和常量定义:
        • 2.边的结构体:
        • 3.全局变量:
        • 4.Bellman-Ford算法实现:
        • 5.主函数:
    • 注意事项
      • 代码含义
      • 为什么需要 backup[a]?
      • 举例说明
      • 关键点
    • 总结

解析

要实现从1号点到n号点最多经过k条边的最短距离,可以使用Bellman-Ford算法的变种,限制松弛操作的次数为k次。通过备份距离数组确保每次松弛操作仅基于上一轮的结果,避免路径边数超过限制。具体步骤如下:

方法思路

  1. 初始化距离数组:将源点1的距离设为0,其余点设为无穷大。

  2. 松弛操作:进行k次迭代,每次遍历所有边,使用备份数组确保每次仅更新上一步的结果。

  3. 负权处理:允许负权边,但不处理负权环,因为边数被限制为k次。

  4. 结果判断:若目标点的距离仍为初始无穷大值,则不可达,否则输出结果。

解决代码

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 510, M = 10010; // 定义最大点数和边数
const int INF = 0x3f3f3f3f;   // 定义无穷大值

struct Edge {
    int a, b, w; // 边的起点、终点和权重
} edges[M];      // 存储所有边的数组

int n, m, k;     // n: 点数,m: 边数,k: 最多经过的边数
int dist[N];     // 存储从起点到每个点的最短距离
int backup[N];   // 备份数组,用于保存上一轮迭代的距离

// Bellman-Ford算法实现
void bellman_ford() {
    memset(dist, 0x3f, sizeof dist); // 初始化距离数组为无穷大
    dist[1] = 0; // 起点到自身的距离为0

    // 进行k次松弛操作
    for (int i = 0; i < k; ++i) {
        memcpy(backup, dist, sizeof dist); // 将当前距离数组复制到备份数组
        for (int j = 0; j < m; ++j) {      // 遍历所有边
            int a = edges[j].a, b = edges[j].b, w = edges[j].w;
            if (backup[a] != INF) { // 如果上一轮中a是可达的
                dist[b] = min(dist[b], backup[a] + w); // 更新b的距离
            }
        }
    }
}

int main() {
    // 输入点数、边数和最多经过的边数
    scanf("%d%d%d", &n, &m, &k);
    // 输入每条边的信息
    for (int i = 0; i < m; ++i) {
        int a, b, w;
        scanf("%d%d%d", &a, &b, &w);
        edges[i] = {a, b, w}; // 存储边
    }

    // 调用Bellman-Ford算法
    bellman_ford();

    // 输出结果
    if (dist[n] == INF) puts("impossible"); // 如果终点不可达,输出impossible
    else printf("%d\n", dist[n]);           // 否则输出最短距离

    return 0;
}

代码解释

  1. 数据结构:使用结构体Edge存储边的起点、终点和权重。

  2. 距离初始化:dist数组初始化为无穷大,源点距离为0。

  3. 松弛操作:进行k次迭代,每次使用备份数组确保仅基于上一步结果更新。

  4. 可达性判断:若目标点距离仍为初始无穷大,则不可达,否则输出最短距离。

该方法确保在最多k条边的限制下找到最短路径,同时正确处理负权边,避免负权环的干扰。

代码逐行注释

1. 头文件和常量定义:
#include <iostream>
#include <cstring>
#include <algorithm>
  • 包含输入输出、字符串操作和算法库。
const int N = 510, M = 10010;
const int INF = 0x3f3f3f3f;
  • N:最大点数。

  • M:最大边数。

  • INF:表示无穷大的值。

2.边的结构体:
struct Edge {
    int a, b, w; // 边的起点、终点和权重
} edges[M];      // 存储所有边的数组
  • a:边的起点。

  • b:边的终点。

  • w:边的权重。

3.全局变量:
int n, m, k;     // n: 点数,m: 边数,k: 最多经过的边数
int dist[N];     // 存储从起点到每个点的最短距离
int backup[N];   // 备份数组,用于保存上一轮迭代的距离
  • dist:存储从起点到每个点的最短距离。

  • backup:备份数组,用于保存上一轮迭代的结果。

4.Bellman-Ford算法实现:
void bellman_ford() {
    memset(dist, 0x3f, sizeof dist); // 初始化距离数组为无穷大
    dist[1] = 0; // 起点到自身的距离为0
  • 初始化dist数组,所有点的距离设为无穷大,起点的距离设为0。
    for (int i = 0; i < k; ++i) { // 进行k次松弛操作
        memcpy(backup, dist, sizeof dist); // 将当前距离数组复制到备份数组
        for (int j = 0; j < m; ++j) {      // 遍历所有边
            int a = edges[j].a, b = edges[j].b, w = edges[j].w;
            if (backup[a] != INF) { // 如果上一轮中a是可达的
                dist[b] = min(dist[b], backup[a] + w); // 更新b的距离
            }
        }
    }
}
  • 进行k次松弛操作。

  • 每次迭代前,将dist数组复制到backup数组。

  • 遍历所有边,基于backup数组更新dist数组。

5.主函数:
int main() {
    scanf("%d%d%d", &n, &m, &k); // 输入点数、边数和最多经过的边数
    for (int i = 0; i < m; ++i) { // 输入每条边的信息
        int a, b, w;
        scanf("%d%d%d", &a, &b, &w);
        edges[i] = {a, b, w}; // 存储边
    }

    bellman_ford(); // 调用Bellman-Ford算法

    if (dist[n] == INF) puts("impossible"); // 如果终点不可达,输出impossible
    else printf("%d\n", dist[n]);           // 否则输出最短距离

    return 0;
}
  • 输入点数、边数和最多经过的边数。

  • 输入每条边的信息并存储。

  • 调用bellman_ford函数计算最短距离。

  • 根据结果输出impossible或最短距离。

注意事项

dist[b] = min(dist[b], backup[a] + w);Bellman-Ford算法中的松弛操作,它的作用是尝试通过边a → b来缩短从起点到点 b 的最短距离。下面详细解释这行代码的含义:


代码含义

  • dist[b]:当前从起点到点 b 的最短距离。

  • backup[a]:上一轮迭代中从起点到点 a 的最短距离。

  • w:边 a → b 的权重。

  • backup[a] + w:通过边 a → b 到达点 b 的路径距离。

  • min(dist[b], backup[a] + w):比较当前的最短距离和通过边 a → b 的新路径距离,取较小值。

这行代码的意思是:如果通过边 a → b 可以缩短从起点到点 b 的距离,则更新 dist[b]。


为什么需要 backup[a]?

  • backup 数组保存的是上一轮迭代的结果。

  • 在 Bellman-Ford 算法中,每次迭代只能增加一条边到路径中。如果直接使用 dist[a] 更新 dist[b],可能会导致在同一轮迭代中多次更新同一条路径,从而违反边数限制。

  • 通过使用 backup[a],我们确保每次更新都基于上一轮的结果,而不是当前轮次中已经更新过的值。


举例说明

假设有以下图:

起点:1
边:1 → 2 (权重 1)
     2 → 3 (权重 1)
     1 → 3 (权重 3)

限制边数k = 2

初始状态

  • dist 数组:[0, INF, INF](起点到自身的距离为 0,其余为无穷大)。

  • backup 数组:与 dist 相同。

第一轮迭代(k=1)

  • 遍历所有边:

    • 边 1 → 2:dist[2] = min(INF, 0 + 1) = 1。

    • 边 2 → 3:dist[3] = min(INF, INF + 1) = INF(因为 backup[2] = INF,不可达)。

    • 边 1 → 3:dist[3] = min(INF, 0 + 3) = 3。

  • 更新后的 dist 数组:[0, 1, 3]。

第二轮迭代(k=2)

  • 将 dist 复制到 backup:backup = [0, 1, 3]。

  • 遍历所有边:

    • 边 1 → 2:dist[2] = min(1, 0 + 1) = 1(无变化)。

    • 边 2 → 3:dist[3] = min(3, 1 + 1) = 2(通过路径 1 → 2 → 3 更新)。

    • 边 1 → 3:dist[3] = min(2, 0 + 3) = 2(无变化)。

  • 更新后的 dist 数组:[0, 1, 2]。

结果

  • 从起点到点 3 的最短距离为 2,路径为 1 → 2 → 3。

关键点

  1. 松弛操作:通过边 a → b 尝试缩短从起点到点 b 的距离。

  2. backup 的作用:确保每次更新都基于上一轮的结果,避免在同一轮迭代中多次更新同一条路径。

  3. 边数限制:通过限制迭代次数 k,确保路径的边数不超过 k。

总结

  • backup数组的作用:保存上一轮迭代的结果,确保每次更新都基于上一轮的结果,避免路径边数超过限制。

  • 时间复杂度:O(k * m),其中k是限制的边数,m是边数。

  • 适用场景:适用于有向图,允许负权边,限制路径边数的最短路径问题。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

6 [新一代Github投毒针对网络安全人员钓鱼]

0x01 前言 在Github上APT组织“海莲花”发布存在后门的提权BOF&#xff0c;通过该项目针对网络安全从业人员进行钓鱼。不过其实早在几年前就已经有人对Visual Studio项目恶意利用进行过研究&#xff0c;所以投毒的手法也不算是新的技术。但这次国内有大量的安全从业者转发该钓…

加载数据,并切分

# Step 3 . WebBaseLoader 配置为专门从 Lilian Weng 的博客文章中抓取和加载内容。它仅针对网页的相关部分&#xff08;例如帖子内容、标题和标头&#xff09;进行处理。 加载信息 from langchain_community.document_loaders import WebBaseLoader loader WebBaseLoader(w…

【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】2.5 高级索引应用:图像处理中的区域提取

2.5 高级索引应用&#xff1a;图像处理中的区域提取 目录/提纲 #mermaid-svg-BI09xc20YqcpUam7 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-BI09xc20YqcpUam7 .error-icon{fill:#552222;}#mermaid-svg-BI09xc20…

房屋中介管理系统的设计与实现

房屋中介管理系统的设计与实现 摘要&#xff1a;随着房地产市场的快速发展&#xff0c;房屋中介行业的信息管理需求日益增长。传统的管理方式已无法满足中介公司对房源信息、客户信息以及业务流程的高效管理需求。为此&#xff0c;本文设计并实现了一套房屋中介管理系统&#x…

Vue指令v-on

目录 一、Vue中的v-on指令是什么&#xff1f;二、v-on指令的简写三、v-on指令的使用 一、Vue中的v-on指令是什么&#xff1f; v-on指令的作用是&#xff1a;为元素绑定事件。 二、v-on指令的简写 “v-on&#xff1a;“指令可以简写为”” 三、v-on指令的使用 1、v-on指令绑…

力扣第435场周赛讲解

文章目录 题目总览题目详解3442.奇偶频次间的最大差值I3443.K次修改后的最大曼哈顿距离3444. 使数组包含目标值倍数的最少增量3445.奇偶频次间的最大差值 II 题目总览 奇偶频次间的最大差值I K次修改后的最大曼哈顿距离 使数组包含目标值倍数的最少增量 奇偶频次间的最大差值I…

编程AI深度实战:给vim装上AI

系列文章&#xff1a; 编程AI深度实战&#xff1a;私有模型deep seek r1&#xff0c;必会ollama-CSDN博客 编程AI深度实战&#xff1a;自己的AI&#xff0c;必会LangChain-CSDN博客 编程AI深度实战&#xff1a;给vim装上AI-CSDN博客 编程AI深度实战&#xff1a;火的编程AI&…

嵌入式知识点总结 操作系统 专题提升(四)-上下文

针对于嵌入式软件杂乱的知识点总结起来&#xff0c;提供给读者学习复习对下述内容的强化。 目录 1.上下文有哪些?怎么理解? 2.为什么会有上下文这种概念? 3.什么情况下进行用户态到内核态的切换? 4.中断上下文代码中有哪些注意事项&#xff1f; 5.请问线程需要保存哪些…

python算法和数据结构刷题[6]:二叉树、堆、BFS\DFS

遍历二叉树 前序遍历NLR&#xff1a;先访问根结点&#xff0c;再前序遍历左子树&#xff0c;最后前序遍历右子树。中序遍历LNR&#xff1a;先中序遍历左子树&#xff0c;再访问根结点&#xff0c;最后中序遍历右子树。后序遍历 LRN&#xff1a;先后序遍历左子树&#xff0c;再…

012-51单片机CLD1602显示万年历+闹钟+农历+整点报时

1. 硬件设计 硬件是我自己设计的一个通用的51单片机开发平台&#xff0c;可以根据需要自行焊接模块&#xff0c;这是用立创EDA画的一个双层PCB板&#xff0c;所以模块都是插针式&#xff0c;不是表贴的。电路原理图在文末的链接里&#xff0c;PCB图暂时不选择开源。 B站上传的…

w191教师工作量管理系统的设计与实现

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;原创团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文…

Python 网络爬虫实战:从基础到高级爬取技术

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 1. 引言 网络爬虫&#xff08;Web Scraping&#xff09;是一种自动化技术&#xff0c;利用程序从网页中提取数据&#xff0c;广泛…

[漏洞篇]SQL注入漏洞详解

[漏洞篇]SQL注入漏洞详解 介绍 把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串&#xff0c;最终达到欺骗服务器执行恶意的SQL命令。通过构造恶意的输入&#xff0c;使数据库执行恶意命令&#xff0c;造成数据泄露或者修改内容等&#xff0c;以达到攻击的目的。…

C#,shell32 + 调用控制面板项(.Cpl)实现“新建快捷方式对话框”(全网首发)

Made By 于子轩&#xff0c;2025.2.2 不管是使用System.IO命名空间下的File类来创建快捷方式文件&#xff0c;或是使用Windows Script Host对象创建快捷方式&#xff0c;亦或是使用Shell32对象创建快捷方式&#xff0c;都对用户很不友好&#xff0c;今天小编为大家带来一种全新…

DDD - 微服务架构模型_领域驱动设计(DDD)分层架构 vs 整洁架构(洋葱架构) vs 六边形架构(端口-适配器架构)

文章目录 引言1. 概述2. 领域驱动设计&#xff08;DDD&#xff09;分层架构模型2.1 DDD的核心概念2.2 DDD架构分层解析 3. 整洁架构&#xff1a;洋葱架构与依赖倒置3.1 整洁架构的核心思想3.2 整洁架构的层次结构 4. 六边形架构&#xff1a;解耦核心业务与外部系统4.1 六边形架…

基于SpringBoot的新闻资讯系统的设计与实现(源码+SQL脚本+LW+部署讲解等)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

xmind使用教程

xmind使用教程 前言xmind版本信息“xmind使用教程”的xmind思维导图 前言 首先xmind是什么&#xff1f;XMind 是一款思维导图和头脑风暴工具&#xff0c;用于帮助用户组织和可视化思维、创意和信息。它允许用户通过图形化的方式来创建、整理和分享思维导图&#xff0c;可以用于…

半导体器件与物理篇7 微波二极管、量子效应和热电子器件

基本微波技术 微波频率&#xff1a;微波频率涵盖约从0.1GHz到3000GHz&#xff0c;相当于波长从300cm到0.01cm。 分布效应&#xff1a;电子部件在微波频率&#xff0c;与其在较低频率的工作行为不同。 输运线&#xff1a;一个由电阻、电容、电感三种等效基本电路部件所组成的…

Java自定义IO密集型和CPU密集型线程池

文章目录 前言线程池各类场景描述常见场景案例设计思路公共类自定义工厂类-MyThreadFactory自定义拒绝策略-RejectedExecutionHandlerFactory自定义阻塞队列-TaskQueue&#xff08;实现 核心线程->最大线程数->队列&#xff09; 场景1&#xff1a;CPU密集型场景思路&…

浅谈线段树

文章同步发布于洛谷&#xff0c;建议前往洛谷查看。 前言 蒟蒻终于学会线段树&#xff08;指【模板】线段树 1 1 1&#xff09;啦&#xff01; 线段树思想 我们先来考虑 P3372&#xff08;基础线段树模板题&#xff09;给的操作&#xff1a; 区间修改&#xff08;增加&am…