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

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;
        }
    }
    

总结

  • 掌握关键点

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

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

相关文章

动手实现自己的 JVM——Go!(ch01)

动手实现自己的 JVM——Go&#xff01;&#xff08;ch01&#xff09; 参考张秀宏老师的《自己动手写java虚拟机》 为什么需要命令行 在 JMV 中&#xff0c;要运行一个 Java 文件&#xff08;字节码&#xff09;&#xff0c;首先需要找到这个文件。那么&#xff0c;如何找到文件…

IIS部署netcore程序后,出现500.30错误解决方案之一

netcore程序部署到IIS后一直出现错误&#xff0c;访问首页后会跳转到登录页地址&#xff0c;然后看到如下错误 HTTP Error 500.30 - ANCM In-Process Start Failure Common solutions to this issue: The application failed to start The application started but then stopp…

将Docker容器打包成镜像提交

前言 Docker 是一个开源软件&#xff0c;也是一个开放平台&#xff0c;用于开发应用、交付&#xff08;shipping&#xff09;应用、运行应用。 Docker允许用户将基础设施&#xff08;Infrastructure&#xff09;中的应用单独分割出来&#xff0c;形成更小的颗粒&#xff08;容…

【设计模式】【行为型模式】命令模式(Command)

&#x1f44b;hi&#xff0c;我不是一名外包公司的员工&#xff0c;也不会偷吃茶水间的零食&#xff0c;我的梦想是能写高端CRUD &#x1f525; 2025本人正在沉淀中… 博客更新速度 &#x1f44d; 欢迎点赞、收藏、关注&#xff0c;跟上我的更新节奏 &#x1f3b5; 当你的天空突…

【DDD系列-3】DDD战术设计实践分享

DDD 战术设计概念​ ​ ​​ TMF2 中的概念&#xff1a;​ 领域能力&#xff1a;​ 扩展点&#xff1a;​ DDD 战术设计使用场景​ 复杂业务场景​ 复杂来源面对的需求方更加多样化。​ 1 相同场景不同垂直业务方的需求&#xff08;1688&#xff0c;淘宝&#xff0c;天…

基于单片机的仓库安防系统(论文+源码)

2.1 需求分析 仓库由于存有大量物品&#xff0c;因此对仓库的监控非常重要&#xff0c;目前仓库已经普遍装有安防系统&#xff0c;以保证仓库的安全&#xff0c;本次基于单片机的仓库安防系统设计&#xff0c;在功能上设计如下&#xff1a; 用户可通过IC卡进入仓库&#xff1…

使用 AutoMQ 和 Tinybird 分析用户网购行为

前言 在当前竞争激烈的市场环境中&#xff0c;数据分析已成为企业实现差异化和精准营销的关键。通过分析用户行为数据&#xff0c;企业能够深入了解用户的习惯、偏好和行为模式&#xff0c;从而更精准地定位目标市场&#xff0c;制定个性化营销策略&#xff0c;并提供定制化推…

小米 R3G 路由器刷机教程(Pandavan)

小米 R3G 路由器刷机教程&#xff08;Pandavan&#xff09; 一、前言 小米 R3G 路由器以其高性价比和稳定的性能备受用户青睐。然而&#xff0c;原厂固件的功能相对有限&#xff0c;难以满足高级用户的个性化需求。刷机不仅可以解锁路由器的潜能&#xff0c;还能通过第三方固…

Python数据可视化 - Matplotlib教程

文章目录 前言一、Matplotlib简介及安装1. Matplotlib简介2. 安装Matplotlib 二、Matplotlib Pyplot1. Pyplot介绍2. Pyplot中方法介绍2.1 创建和管理图形2.2 绘制图形2.3 设置图形属性2.4 保存和展示 三、Matplotlib绘图标记1. 介绍2. 基本用法3. 标记大小与颜色4. 标记样式列…

DeepSeek 与网络安全:AI 驱动的智能防御

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 1. 引言 随着人工智能&#xff08;AI&#xff09;的快速发展&#xff0c;深度学习技术正渗透到多个领域&#xff0c;从医疗诊断到…

STM32——HAL库开发笔记19(串口中断接收实验)(参考来源:b站铁头山羊)

本实验&#xff0c;我们以中断的方式使得串口发送数据控制LED的闪烁速度&#xff0c;发送1&#xff0c;慢闪&#xff1b;发送2&#xff0c;速度正常&#xff1b;发送3&#xff0c;快闪。 一、电路连接图 二、实现思路&CubeMx配置 1、实现控制LED的闪烁速度 uint32_t bli…

开关电源实战(一)宽范围DC降压模块MP4560

系列文章目录 文章目录 系列文章目录MP4560MP4560 3.8V 至 55V 的宽输入范围可满足各种降压应用 MOSFET只有250mΩ 输出可调0.8V-52V SW:需要低VF肖特基二极管接地,而且要靠近引脚,高压侧开关的输出。 EN:输入使能,拉低到阈值以下关闭芯片,拉高或浮空启动 COMP:Compens…

网络IP地址冲突故障,快速解决方案!

由于网络被广泛运用&#xff0c;网络规模持续变大&#xff0c;对应的 IP 地址分配也越来越多&#xff0c;IP 地址冲突的情况日益严重&#xff0c;在一定程度上对网络的正常运行造成了影响。 要维护网络稳定、高效地运行&#xff0c;解决 IP 地址冲突的问题就成了网络管理里的一…

C++模拟实现二叉搜索树

目录 1.二叉搜索树概念 2.二叉搜索树的实现 2.1二叉搜索树的查找 2.2二叉树的插入 2.3二叉树的删除 3.所有代码 4.二叉搜索树的应用 5.二叉搜索树的性能分析 1.二叉搜索树概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二…

3D渐变柱状图

代码说明 数据准备&#xff1a; 数据可以是任意形式的矩阵&#xff0c;例如 5x7 的矩阵。 行标签 (rowLabels) 和列标签 (colLabels) 是可选的&#xff0c;如果不需要可以删除相关部分。 颜色定义&#xff1a; 使用自定义的蓝黄渐变色 (map)。 如果需要其他颜色&#xff0c;…

完美解决 error:0308010C:digital envelope routines::unsupported

查看专栏目录 Network 灰鸽宝典专栏主要关注服务器的配置&#xff0c;前后端开发环境的配置&#xff0c;编辑器的配置&#xff0c;网络服务的配置&#xff0c;网络命令的应用与配置&#xff0c;windows常见问题的解决等。 文章目录 windows电脑完美解决办法&#xff1a;设置说明…

Xilinx kintex-7系列 FPGA支持PCIe 3.0 吗?

Xilinx kintex-7系列资源如下图 Xilinx各系列的GT资源类型和性能 PCIe Gen1/2/3的传输速率对比 K7上面使用的高速收发器GTX最高速率为12.5GT/s&#xff0c; PCIe Gen2 每个通道的传输速率为 5 GT/s。 PCIe Gen3 每个通道的传输速率为 8 GT/s。 所以理论上硬件支持PCIe3.0&#…

支持列表拖拽嵌套,AI流式输出的多模态文档编辑器flowmix/docx: 全面升级

hi, 大家好, 我是徐小夕. 马上又到周五了, 最近也收到很多用户对 flowmix/docx 多模态文档编辑器的反馈&#xff0c;我们也做了一波新功能的升级&#xff0c;今天就和大家分享一下 flowmix/docx 多模态文档编辑器的最新更新. 演示地址: https://flowmix.turntip.cn/docx 以下是…

Mysql中使用sql语句生成雪花算法Id

&#x1f353; 简介&#xff1a;java系列技术分享(&#x1f449;持续更新中…&#x1f525;) &#x1f353; 初衷:一起学习、一起进步、坚持不懈 &#x1f353; 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正&#x1f64f; &#x1f353; 希望这篇文章对你有所帮助,欢…

聊聊 IP 地址和端口号的区别

在计算机网络中&#xff0c;两个基本概念对于理解设备如何通过网络进行通信至关重要。IP 地址和端口号是 TCP/IP 的典型特征&#xff0c;其定义如下&#xff1a;IP 地址是分配给连接到网络的每台机器的唯一地址&#xff0c;用于定位机器并与其通信。相反&#xff0c;端口号用于…