Day65_20250213图论part9_dijkstra(堆优化版)|Bellman_ford算法精讲

news2025/4/16 13:52:02

Day65_20250213图论part9_dijkstra(堆优化版)|Bellman_ford算法精讲

dijkstra(堆优化版)

题目

https://www.programmercarl.com/kamacoder/0047.%E5%8F%82%E4%BC%9Adijkstra%E5%A0%86.html

小明参加科学大会

思路

  • 思路

    • 朴素版的dijkstra,时间复杂度为O(n^2),与节点数量有关系

    • 从边的数量出发来优化算法

      • 当n很大,边数量也很大,保持原算法。
      • 当n很小,边很小(稀疏图),从边的角度来求最短路。
    • 图的存储

      • 邻接矩阵
        • 二维数组,节点角度,有多少节点就申请多大的二维数组。
        • 缺点
          • 稀疏图,边少节点多,申请过大的二维数组,空间浪费
          • 时间复杂度n*n
        • 优点
          • 简单
          • 检查任意两个顶点间是否存在边的操作非常快
          • 适合稠密图(边数接近顶点数平方)
      • 邻接表 数组+链表
        • 从边的数量来表示图,边数量决定链表大小
        • 优点:
          • 稀疏图(边少节点多)的存储,只要存储边,空间利用率高
          • 遍历节点的链接情况更容易
        • 缺点:
          • 检查任意两个节点间是否存在边,效率相对低,时间复杂度O(V),V表示某节点连接其他节点的数量
          • 实现复杂,不易理解
    • 三部曲(从节点角度)

      • 选源点到哪个节点近且该节点未被访问过。
      • 该最近节点被标记访问过
      • 更新非访问节点到源点的距离(更新minDist数组)
    • 三部曲(从边角度) 稀疏图

      • 直接把边(带权值)加入到小顶堆(利用堆来自动排序),每次从堆顶里取出边自然是距离源点最近的节点所在的边。
        • 不需要两层for循环
      • 用邻接表来表述图结构,链表中的数是一个键值对
        • 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
        • 在代码中使用pair <int,int>容易搞混int的两个表示,代码可读性查,可以自定义一个类来取代pair<int,int>结构体
  • 细节

    • 遍历节点用for循环,而遍历边遍历的是链表grid[cur节点编号] 【要对邻接表的表达方式有了解】

      • for (Edge edge : grid[cur.first])
      • pair<节点编号,源点到该节点的权值>
    • 与朴素版的dijkstra

      • 邻接表的表示方式不同
      • 使用优先级队列(小顶栈)来堆新链接的边排序
      • 复杂度
        • 时间复杂度:O(ElogE) E为边的数量
        • 空间复杂度:O(N+E) N为节点的数量
  • 代码

    import java.util.*;
    public class Main{
        public static void main(String[] args){
            //输入
            Scanner scanner=new Scanner(System.in);
            int n=scanner.nextInt();
            int m=scanner.nextInt();
        
            //边
            List<List<Edge>> grid=new ArrayList<>(n+1);
            for(int i=0;i<=n;i++){
                grid.add(new ArrayList<>());
            }
            for(int i=0;i<m;i++){
                int p1=scanner.nextInt();
                int p2=scanner.nextInt();
                int val=scanner.nextInt();
                grid.get(p1).add(new Edge(p2,val));
            }
        
            //起点和终点
            int start=1;
            int end=n;
        
            //存储从源点到每个节点的最短距离
            int[] minDist=new int[n+1];
            Arrays.fill(minDist,Integer.MAX_VALUE);
        
            //记录顶点是否被访问过
            boolean[] visited=new boolean[n+1];
            //优先队列中存放Pair<节点,源点到该节点的权值?
            PriorityQueue<Pair<Integer,Integer>> pq=new PriorityQueue<>(new MyComparison());
            //初始化队列,源点到源点的距离为0,初始为0
            pq.add(new Pair<>(start,0));
        
            minDist[start]=0;//起始点到自身的距离为0
            //当小顶栈不为空
            while(!pq.isEmpty()){
                //1.第一步,选源点到哪个节点近&&未被访问过(优先级队列实现)
                Pair<Integer,Integer> cur=pq.poll();
                if(visited[cur.first])   continue;
                //第二步,该最近节点被标记访问过
                visited[cur.first]=true;
                //第三步,更新非访问节点到源点的距离(更新minDist数组)
                for(Edge edge:grid.get(cur.first)){
                    //选中的cur节点未被访问&&【新路径比之前更短】源点到当前节点的最短距离加上当前节点的值比之前的更小
                    if(!visited[edge.to]&&minDist[cur.first]+edge.val<minDist[edge.to]){
                        minDist[edge.to]=minDist[cur.first]+edge.val;//更新
                        //为了确保下一次能够处理这个新的最短路径,必须将更新后的节点重新放入优先队列。
                        pq.add(new Pair<>(edge.to,minDist[edge.to]));
                    }
                }
            }
            //打印
            if(minDist[end]==Integer.MAX_VALUE){
                System.out.println(-1);//不能到达终点
            }else{
                System.out.println(minDist[end]);//到达终点最短路径
            }
        
        
        
        
        }
    }
    //结构体
    class Edge{
        int to;//邻接顶点
        int val;//边的权重
      
        Edge(int to,int val){
            this.to=to;
            this.val=val;
        }
    }
    
    class MyComparison implements Comparator<Pair<Integer, Integer>> {
        @Override
        public int compare(Pair<Integer, Integer> lhs, Pair<Integer, Integer> rhs) {
            return Integer.compare(lhs.second, rhs.second);
        }
    }
    
    class Pair<U, V> {
        public final U first;
        public final V second;
    
        public Pair(U first, V second) {
            this.first = first;
            this.second = second;
        }
    }
    
    
    

总结

  • 与朴素版的代码,邻接表的表示方式不同。
  • 使用优先级队列(小顶栈)来堆新链接的边排序

Bellman_ford算法

题目

【城市间货物运输I】

题目描述

某国为促进城市间经济交流,决定对货物运输提供补贴。共有 n 个编号为 1 到 n 的城市,通过道路网络连接,网络中的道路仅允许从某个城市单向通行到另一个城市,不能反向通行。

网络中的道路都有各自的运输成本和政府补贴, 道路的权值计算方式为:运输成本 - 政府补贴 。权值为正表示扣除了政府补贴后运输货物仍需支付的费用;权值为负则表示政府的补贴超过了支出的运输成本,实际表现为运输过程中还能赚取一定的收益。

请找出从城市 1 到城市 n 的所有可能路径中,综合政府补贴后的最低运输成本。如果最低运输成本是一个负数,它表示在遵循最优路径的情况下,运输过程中反而能够实现盈利。

城市 1 到城市 n 之间可能会出现没有路径的情况,同时保证道路网络中不存在任何负权回路。

输入描述

第一行包含两个正整数,第一个正整数 n 表示该国一共有 n 个城市,第二个整数 m 表示这些城市中共有 m 条道路。

接下来为 m 行,每行包括三个整数,s、t 和 v,表示 s 号城市运输货物到达 t 号城市,道路权值为 v (单向图)。

输出描述

如果能够从城市 1 到连通到城市 n, 请输出一个整数,表示运输成本。如果该整数是负数,则表示实现了盈利。如果从城市 1 没有路径可达城市 n,请输出 “unconnected”。

输入示例
6 7
5 6 -2
1 2 1
5 3 1
2 5 2
2 4 -3
4 6 4
1 3 5
输出示例
1
提示信息

示例中最佳路径是从 1 -> 2 -> 5 -> 6,路上的权值分别为 1 2 -2,最终的最低运输成本为 1 + 2 + (-2) = 1。

示例 2:

4 2
1 2 -1
3 4 -1

在此示例中,无法找到一条路径从 1 通往 4,所以此时应该输出 “unconnected”。

数据范围:

1 <= n <= 1000;
1 <= m <= 10000;

-100 <= v <= 100;

思路

  • 思路
    • **【带负权值的单源最短路问题】**单源最短路问题,边的权值是有负数的。【运输的过程中政府补贴>运输成本】

    • 核心:****【松弛】****对所有边进行松弛n-1次操作(n为节点数量),从而求得目标最短路。

      • if (minDist[B] > minDist[A] + value) minDist[B] = minDist[A] + value
      • 状态一::minDist[A]+valur推出minDist[B] 状态二:minDist[B]本身就有权值(其他边连接到节点C,minDist[B]记录了其他边到minDist[B]的权值)
      • minDist[B]=min(minDist[A]+value,minDist[B]);
      • 动态规划:将一个问题分解成多个决策阶段,通过状态之间的递归关系最后计算出全局最优解。、
    • 为什么是n-1次松弛?

      • 对所有边松弛一次,相当于计算起点到达与起点一条边相连的节点的最短距离。
      • 对所有边松弛n-1次,得到与起点n-1边相连的节点的最短距离。
      • 节点数量是n,从起点到终点最多是n-1条边相连。无论图是什么样的,边是什么样的顺序,对所有边松弛n-1次就一定能得到起点到终点的最短距离。
      • 当松弛大于n-1次,minDist数组就不会变化了。同时保证道路网络中不存在任何负权回路(在有向图中出现有向环且环的总权值为负数),只需要松弛n-1次就能得到结果,不需要松弛更多次了。
    • 复杂度

      • 时间复杂度:O(N*E),N为节点数量,E是图中边的数量
      • 空间复杂度:O(N),N节点,minDist数组所开辟的空间
  • 代码
    import java.util.*;
    
    public class Main{
        public static void main(String[] args){
            //输入
            Scanner scanner=new Scanner(System.in);
            int n=scanner.nextInt();//节点
            int m=scanner.nextInt();//边
            List<Edge> edges=new ArrayList<>();
            for(int i=0;i<m;i++){
                int from=scanner.nextInt();
                int to=scanner.nextInt();
                int val=scanner.nextInt();
                edges.add(new Edge(from,to,val));
            }
        
            int[] minDist=new int[n+1];//从源点到当前节点的最短路径
            //初始化
            Arrays.fill(minDist,Integer.MAX_VALUE);
            minDist[1]=0;//从1开始
        
            //关键代码
            for(int i=1;i<n;i++){
                for(Edge edge:edges){
                //更新minDist矩阵
                //从遍历过的节点出发&&当前边的的起点+一个路径的终点的值<当前终点的
                //1.没有从源点到edge.from的有效路径。无法通过edge.from进行有效的路径更新
                //2.检查通过当前边edge是否可以得到更短的路径(如果这个路径长度<当前存储在minDist[edge.to]的值)
                    if(minDist[edge.from]!=Integer.MAX_VALUE&&(minDist[edge.from]+edge.val)<minDist[edge.to]){
                        //更新当前节点的最短路径,从A得到的B和B本身进行比较
                        minDist[edge.to]=minDist[edge.from]+edge.val;
                    }
                }
            }
            //打印结果
            if(minDist[n]==Integer.MAX_VALUE){
                System.out.println("unconnected");
            }else{
                System.out.println(minDist[n]);
            }
        }
      
    }
    
    class Edge{
        int from;
        int to;
        int val;
        public Edge(int from,int to,int val){
            this.from=from;
            this.to=to;
            this.val=val;
        }
    }
    

总结

  • 掌握关键点

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

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

相关文章

w208基于spring boot物流管理系统设计与实现

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

望远镜成像系统--科学评价光学镜头

望远镜是一种利用透镜或反射镜以及其他光学器件观测遥远物体的光学仪器。其原理是通过透镜的折射或反射镜的反射&#xff0c;将光线聚焦成像&#xff0c;再经过一个放大目镜进行观察。日常生活中的光学望远镜又称“天文望远镜”。1608年&#xff0c;荷兰的一位眼镜商汉斯利伯希…

产品更新 | 华望M-Design 平台的AI 建模功能即将上线

前言 在 AI 技术加速发展的背景下&#xff0c;杭州华望系统科技有限公司在⼤语⾔模型与 SysML 标准进行深度结合的基础上&#xff0c;强力推出AI建模功能。该功能⽀持⽤户通过上传⽂档或对话交互等⽅式完成需求智能增强、模型动态构建与细节补充、实时获取结构化反馈等业务。⽬…

RabbitMQ 在 Spring Boot中使用方式

文章目录 作用MQ docker 安装MQ使用RabbitMQ的整体架构及核心概念&#xff1a;RabbitMQ的整体架构及核心概念&#xff1a;消费者消息推送限制交换机与队列## 项目使用MQDirect: 直连模式Fanout: 广播模式Topic: 主题模式Headers: 头信息模式 使用DEMO地址异常问题记录 作用 Ra…

HAL库框架学习总结

概述&#xff1a;HAL库为各种外设基本都配了三套 API&#xff0c;查询&#xff0c;中断和 DMA。 一、HAL库为外设初始化提供了一套框架&#xff0c;这里以串口为例进行说明&#xff0c;调用函数 HAL_UART_Init初始化串口&#xff0c;此函数就会调用 HAL_UART_MspInit&#xff0…

深入解析系统调用接口(System Call Interface, SCI)

在操作系统的世界中&#xff0c;用户态应用程序无法直接访问内核态资源&#xff0c;而必须通过一种受控的方式进行交互。这种方式就是系统调用&#xff08;System Call&#xff09;。系统调用接口&#xff08;System Call Interface, SCI&#xff09;是用户程序与操作系统内核之…

深入理解Linux网络随笔(一):内核是如何接收网络包的(下篇)

3、接收网络数据 3.1.1硬中断处理 数据帧从网线到达网卡时候&#xff0c;首先到达网卡的接收队列&#xff0c;网卡会在初始化时分配给自己的RingBuffer中寻找可用内存位置&#xff0c;寻找成功后将数据帧DMA到网卡关联的内存里&#xff0c;DMA操作完成后&#xff0c;网卡会向…

《只狼》运行时提示“mfc140u.dll文件缺失”是什么原因?要怎么解决?

《只狼》运行时提示“mfc140u.dll文件缺失”是什么原因&#xff1f;要怎么解决&#xff1f; 宝子们&#xff0c;是不是在玩《只狼》的时候&#xff0c;突然弹出一个提示&#xff1a;“找不到mfc140u.dll文件”&#xff1f;这可真是让人着急上火&#xff01;别慌&#xff0c;今…

SSM开发(十二) mybatis的动态SQL

目录 一、为什么需要动态SQL? Mybatis 动态 sql 是做什么的? 二、多种动态 SQL 元素 三、示例 1、model定义 2、数据库定义 3、UserMapper接口及UserMapper.xml内容定义 if标签 choose/when/otherwise 标签 foreach标签 trim 标签 四、动态SQL注意 一、为什么需…

基于LVS负载均衡练习

对比 LVS 负载均衡群集的 NAT 模式和 DR 模式&#xff0c;比较其各自的优势。 NAT模式&#xff0c;全称是网络地址转换模式。NAT模式下&#xff0c;负载均衡器&#xff08;Director&#xff09;会修改请求和响应的IP地址。客户端的请求先到达Director&#xff0c;Director将请…

FreeRTOS低功耗总结

前言 Cortex-M核的MCU一般支持以下三种低功耗方式&#xff1a; ● 睡眠(Sleep)模式 ● 停止(Stop)模式 ● 待机(Standby)模式 睡眠模式 进入睡眠模式有两种指令&#xff1a;WFI(等待中断)和WFE(等待事件)&#xff0c; WFI进入睡眠模式后&#xff0c;任意中断都可唤醒。 WFE进…

【IC】AI处理器核心--第二部分 用于处理 DNN 的硬件设计

第 II 部分 用于处理 DNN 的硬件设计 第 3 章 关键指标和设计目标 在过去的几年里&#xff0c;对 DNN 的高效处理进行了大量研究。因此&#xff0c;讨论在比较和评估不同设计和拟议技术的优缺点时应考虑的关键指标非常重要&#xff0c;这些指标应纳入设计考虑中。虽然效率通常…

【python】向Jira测试计划下,附件中增加html测试报告

【python】连接Jira获取token以及jira对象 # 往 jira 测试计划下面&#xff0c;上传测试结果html def put_jira_file(plain_id):# 配置连接jiraconn ConnJira()jira conn.jira_login()[2]path jira.issue(O45- plain_id)attachments_dir os.path.abspath(..) \\test_API…

STM32自学记录(九)

STM32自学记录 文章目录 STM32自学记录前言一、DMA杂记二、实验1.学习视频2.复现代码 总结 前言 DMA 一、DMA杂记 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输&#xff0c;无须CPU干预&…

【C++】C++-教师信息管理系统(含源码+数据文件)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;专__注&#x1f448;&#xff1a;专注主流机器人、人工智能等相关领域的开发、测试技术。 【C】C教师信息管理系统&#xff08;含源码&#x…

Java Swing-5.jar 使用 jpackage 打包成 windows 可安装应用(exe,msi,免安装版exe)

环境 jdk17 (jdk14 以后自带将jar 打安装包工具 jpackage&#xff0c;版本从1.8调整到17) Maven&#xff1a;3.2.5 效果 对比 exe4j :免费版在启动的时候总是先弹出一个弹框&#xff0c;告诉用户你在用他们的免费版Launch4j:无法把jre环境打到exe文件中&#xff0c;用户需要单独…

ADC入门准备(十):信号与系统知识回顾

4.7系统函数零极点分布决定时域特性 4.7.1 H(s)极点分布与h(t)的对应图解 4.7.2 H(s)、E(s&#xff09;极点分布与自由响应、强迫响应特征的对应 4.8 H(s)零极点分布决定频域特性 4.8.1 s平面几何分析法 4.8.2 高通滤波器的频率特性 4.8.3 低通滤波器的频率特性 4.9 二阶谐振系…

wx060基于springboot+vue+uniapp的宿舍报修系统小程序

开发语言&#xff1a;Java框架&#xff1a;springbootuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#…

CCF-GESP 等级考试 2024年9月认证C++二级真题解析

2024年9月真题 一、单选题&#xff08;每题2分&#xff0c;共30分&#xff09; 正确答案&#xff1a;A 考察知识点&#xff1a;计算机存储 解析&#xff1a;磁心存储元件是早期计算机中用于存储数据的部件&#xff0c;它和现代计算机中的内存功能类似&#xff0c;都是用于临时…

第二天:工具的使用

每天上午9点左右更新一到两篇文章到专栏《Python爬虫训练营》中&#xff0c;对于爬虫有兴趣的伙伴可以订阅专栏一起学习&#xff0c;完全免费。 键盘为桨&#xff0c;代码作帆。这趟为期30天左右的Python爬虫特训即将启航&#xff0c;每日解锁新海域&#xff1a;从Requests库的…