码随想录算法训练营第60天|卡码网:94. 城市间货物运输 I、95. 城市间货物运输 II、96. 城市间货物运输 III

news2024/11/18 19:27:05

1.卡码网:94. 城市间货物运输 I

题目链接:https://kamacoder.com/problempage.php?pid=1152
文章链接:https://www.programmercarl.com/kamacoder/0094.城市间货物运输I-SPFA.html

在这里插入图片描述

思路:
只对 上一次松弛的时候更新过的节点作为出发节点所连接的边 进行松弛就够了,不需要对所有边进行松弛。
基于以上思路,如何记录 上次松弛的时候更新过的节点呢?
用队列来记录。(其实用栈也行,对元素顺序没有要求)
针对:图中边的权值可以有负数,且不存在任何负权回路的情况。(可以为正权回路)

import java.util.*;

public class Main {

    // Define an inner class Edge
    static 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;
        }
    }

    public static void main(String[] args) {
        // Input processing
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        List<List<Edge>> graph = new ArrayList<>();

        for (int i = 0; i <= n; i++) {
            graph.add(new ArrayList<>());
        }

        for (int i = 0; i < m; i++) {
            int from = sc.nextInt();
            int to = sc.nextInt();
            int val = sc.nextInt();
            graph.get(from).add(new Edge(from, to, val));
        }

        // Declare the minDist array to record the minimum distance form current node to the original node
        int[] minDist = new int[n + 1];
        Arrays.fill(minDist, Integer.MAX_VALUE);
        minDist[1] = 0;

        // Declare a queue to store the updated nodes instead of traversing all nodes each loop for more efficiency
        Queue<Integer> queue = new LinkedList<>();
        queue.offer(1);

        // Declare a boolean array to record if the current node is in the queue to optimise the processing
        boolean[] isInQueue = new boolean[n + 1];

        while (!queue.isEmpty()) {
            int curNode = queue.poll();
            isInQueue[curNode] = false; // Represents the current node is not in the queue after being polled
            for (Edge edge : graph.get(curNode)) {
                if (minDist[edge.to] > minDist[edge.from] + edge.val) { // Start relaxing the edge
                    minDist[edge.to] = minDist[edge.from] + edge.val;
                    if (!isInQueue[edge.to]) { // Don't add the node if it's already in the queue
                        queue.offer(edge.to);
                        isInQueue[edge.to] = true;
                    }
                }
            }
        }
        
        // Outcome printing
        if (minDist[n] == Integer.MAX_VALUE) {
            System.out.println("unconnected");
        } else {
            System.out.println(minDist[n]);
        }
    }
}

2.卡码网:95. 城市间货物运输 II

题目链接:https://kamacoder.com/problempage.php?pid=1153
文章链接:https://www.programmercarl.com/kamacoder/0095.城市间货物运输II.html

在这里插入图片描述

思路:

  1. 使用 Bellman_ford 算法判断负权回路
    在 bellman_ford 算法中,松弛 n-1 次所有的边 就可以求得 起点到任何节点的最短路径,松弛 n 次以上,minDist数组(记录起到到其他节点的最短距离)中的结果也不会有改变 。
    而本题有负权回路的情况下,一直都会有更短的最短路,所以 松弛 第n次,minDist数组 也会发生改变。
    那么解决本题的 核心思路,就是在 kama94.城市间货物运输I 的基础上,再多松弛一次,看minDist数组 是否发生变化。若变化,则表示出现负权回路;否则,没有。
    注意:若题目中没有提前声明图中是否没有负权回路,则对于 在有负权值的图中求最短路,都需要先看看这个图里有没有负权回路。若有,在这样的图中求最短路的话, 就会在这个环里无限循环 (也是负数+负数 只会越来越小),无法求出最短路径。
import java.util.*;

public class Main {
    public static void main (String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(); // 城市数量
        int m = sc.nextInt(); // 道路数量
        
        List<List<Integer>> roadList = new ArrayList<>();
        for (int i=0;i<m;i++) {
            int s = sc.nextInt();
            int t = sc.nextInt();
            int v = sc.nextInt();
            List<Integer> list = new ArrayList<>();
            list.add(s);
            list.add(t);
            list.add(v);
            roadList.add(list);
        }
        
        int[] minDist = new int[n+1]; // 记录起点到各个节点的最短距离
        Arrays.fill(minDist,Integer.MAX_VALUE);
        minDist[1]=0;
        boolean flag = false; // 判断是否出现负权回路
        
        // 松弛n-1次
        for(int j=1;j<=n;j++) { // 注意这里松弛了n下
            // 对所有道路进行分析
            for(int i=0;i<m;i++) {
                int from = roadList.get(i).get(0);
                int to = roadList.get(i).get(1);
                int value = roadList.get(i).get(2);
                if (j < n) { 
                    if (minDist[from] != Integer.MAX_VALUE && minDist[from] + value < minDist[to]) {
                        minDist[to] = minDist[from] + value;
                        
                    }
                } else { // 多松弛一下,检查是否数组有变化
                    if (minDist[from] != Integer.MAX_VALUE && minDist[from] + value < minDist[to]) {
                       flag = true; // 出现负权回路
                    }
                }
                
            }
        }
        if (flag) {
            System.out.println("circle");
            return;
        }
        
        if (minDist[n] == Integer.MAX_VALUE) {
            System.out.println("unconnected");
        } else {
            System.out.println(minDist[n]);
        }
        
    }
}
  1. 使用 队列优化版的bellman_ford(SPFA)判断是否有负权回路。
    在 0094.城市间货物运输I-SPFA 中,在极端情况下,即:所有节点都与其他节点相连,每个节点的入度为 n-1 (n为节点数量),所以每个节点最多加入 n-1 次队列。
    那么如果节点加入队列的次数 超过了 n-1次 ,那么该图就一定有负权回路。
import java.util.*;

public class Main {
    // 基于Bellman_ford-SPFA方法
    // Define an inner class Edge
    static 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;
        }
    }

    public static void main(String[] args) {
        // Input processing
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        List<List<Edge>> graph = new ArrayList<>();

        for (int i = 0; i <= n; i++) {
            graph.add(new ArrayList<>());
        }

        for (int i = 0; i < m; i++) {
            int from = sc.nextInt();
            int to = sc.nextInt();
            int val = sc.nextInt();
            graph.get(from).add(new Edge(from, to, val));
        }

        // Declare the minDist array to record the minimum distance form current node to the original node
        int[] minDist = new int[n + 1];
        Arrays.fill(minDist, Integer.MAX_VALUE);
        minDist[1] = 0;

        // Declare a queue to store the updated nodes instead of traversing all nodes each loop for more efficiency
        Queue<Integer> queue = new LinkedList<>();
        queue.offer(1);

        // Declare an array to record the times each node has been offered in the queue
        int[] count = new int[n + 1];// 记录节点加入队列几次
        count[1]++;

        // Declare a boolean array to record if the current node is in the queue to optimise the processing
        boolean[] isInQueue = new boolean[n + 1];

        // Declare a boolean value to check if there is a negative weight loop inside the graph
        boolean flag = false;

        while (!queue.isEmpty()) {
            int curNode = queue.poll();
            isInQueue[curNode] = false; // Represents the current node is not in the queue after being polled
            for (Edge edge : graph.get(curNode)) {
                if (minDist[edge.to] > minDist[edge.from] + edge.val) { // Start relaxing the edge
                    minDist[edge.to] = minDist[edge.from] + edge.val;
                    if (!isInQueue[edge.to]) { // Don't add the node if it's already in the queue
                        queue.offer(edge.to);
                        count[edge.to]++;
                        isInQueue[edge.to] = true;
                    }

                    if (count[edge.to] == n) { // 如果加入队列次数超过 n-1次 就说明该图与负权回路
                        flag = true;
                        while (!queue.isEmpty()) queue.poll();
                        break;
                    }
                }
            }
        }

        if (flag) {
            System.out.println("circle");
        } else if (minDist[n] == Integer.MAX_VALUE) {
            System.out.println("unconnected");
        } else {
            System.out.println(minDist[n]);
        }
    }
}

3.卡码网:96. 城市间货物运输 III

题目链接:https://kamacoder.com/problempage.php?pid=1154
文章链接:https://www.programmercarl.com/kamacoder/0096.城市间货物运输III.html

在这里插入图片描述

思路:

  1. 使用 Bellman_ford 算法
    题目中描述是 最多经过 k 个城市的条件下,而不是一定经过k个城市,也可以经过的城市数量比k小,但要最短的路径。
    最多经过k个城市等价于最多经过k + 1 条边。
    由于对所有边松弛一次,相当于计算 起点到达 与起点一条边相连的节点 的最短距离,那么对所有边松弛 k + 1次,就是求 起点到达 与起点k + 1条边相连的节点的 最短距离。若松弛k+1次后,终点城市权值有变化,则表示最多经过k+1条边能够到达终点城市,此时权值就是从起点到终点的最低运输成本;若终点城市权值仍然是初始值,则表示k+1个边无法到达。
    注意:
    第n次松弛必须保证只会更新与起点相连n条边的节点的最短距离,不会更新其他情况的节点,且与起点相连n条边的节点的最短距离是由上一次松弛的结果获得的,而不是当前松弛的结果获得。当前松弛结果只会影响下一次松弛结果。
    所以在每次计算 minDist 时候,要基于 对所有边上一次松弛的 minDist 数值才行,因此我们要记录上一次松弛的minDist。只有这样前面节点松弛的结果才不会影响剩余节点的权值。
import java.util.*;

public class Main {
    public static void main (String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(); // 城市数量
        int m = sc.nextInt(); // 道路数量
        
        List<List<Integer>> roadList = new ArrayList<>();
        for (int i=0;i<m;i++) {
            int s = sc.nextInt();
            int t = sc.nextInt();
            int v = sc.nextInt();
            List<Integer> list = new ArrayList<>();
            list.add(s);
            list.add(t);
            list.add(v);
            roadList.add(list);
        }
        int src = sc.nextInt();
        int dst = sc.nextInt();
        int k = sc.nextInt();
        
        int[] minDist = new int[n+1]; // 记录起点到各个节点的最短距离
        Arrays.fill(minDist,Integer.MAX_VALUE);
        minDist[src]=0;
        
        // 松弛k+1次
        for(int j=1;j<=k+1;j++) {
            int[] minDistCopy = Arrays.copyOf(minDist,minDist.length); // 该数组保证每次松弛节点权值的更新只受上一次松弛结果的影响
            // 对所有道路进行分析
            for(int i=0;i<m;i++) {
                int from = roadList.get(i).get(0);
                int to = roadList.get(i).get(1);
                int value = roadList.get(i).get(2);
                if (minDistCopy[from] != Integer.MAX_VALUE && minDistCopy[from] + value < minDist[to]) {
                    minDist[to] = minDistCopy[from] + value;
                } // 使用上一次松弛的结果使得这次松弛只会更新以上一次松弛更新节点为起点的边的终点节点的权值。
            }
        }
        
        if (minDist[dst] == Integer.MAX_VALUE) {
            System.out.println("unreachable");
        } else {
            System.out.println(minDist[dst]);
        }
        
    }
}
  1. 使用 队列优化版的bellman_ford(SPFA)
    使用SPFA算法解决本题的时候,关键在于 如何控制松弛k次。可以用一个变量 que_size 记录每一轮松弛入队列的所有节点数量。下一轮松弛的时候,就把队列里 que_size 个节点都弹出来,就是上一轮松弛入队列的节点。
import java.util.*;

public class Main {
    static class Edge { // 邻接表中的边
        int to;  // 连接的节点
        int val; // 边的权重

        Edge(int t, int w) {
            to = t;
            val = w;
        }
    }

    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<>();
        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 = scanner.nextInt();
        int end = scanner.nextInt();
        int k = scanner.nextInt();

        k++;

        int[] minDist = new int[n + 1];
        Arrays.fill(minDist, Integer.MAX_VALUE);
        int[] minDistCopy = new int[n + 1]; // 用来记录每一次遍历的结果

        minDist[start] = 0;

        Queue<Integer> queue = new LinkedList<>();
        queue.add(start); // 队列里放入起点

        int queueSize;
        while (k-- > 0 && !queue.isEmpty()) {
            boolean[] visited = new boolean[n + 1]; // 每一轮松弛中,控制节点不用重复入队列
            minDistCopy = minDist.clone(); // 获取上一次计算的结果
            queueSize = queue.size(); // 记录上次入队列的节点个数
            while (queueSize-- > 0) { // 上一轮松弛入队列的节点,这次对应的边都要做松弛
                int node = queue.poll();
                for (Edge edge : grid.get(node)) {
                    int from = node;
                    int to = edge.to;
                    int price = edge.val;
                    if (minDist[to] > minDistCopy[from] + price) {
                        minDist[to] = minDistCopy[from] + price;
                        if (visited[to]) continue; // 不用重复放入队列,但需要重复松弛,所以放在这里位置
                        visited[to] = true;
                        queue.add(to);
                    }
                }
            }
        }

        if (minDist[end] == Integer.MAX_VALUE) {
            System.out.println("unreachable");
        } else {
            System.out.println(minDist[end]);
        }
    }
}

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

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

相关文章

智能餐饮:Spring Boot 点餐系统

第四章 系统设计 4.1 系统体系结构 网上点餐系统的结构图4-1所示&#xff1a; 图4-1 系统结构 模块包括主界面&#xff0c;首页、个人中心、用户管理、美食店管理、美食分类管理、美食信息管理、美食订单管理、美食评价管理、系统管理等进行相应的操作。 登录系统结构图&…

2025年3月PMP考试《PMBOK®指南》第六版不再作为参考资料

大家都知道《PMBOK指南》第六版是PMP认证考试的必备教材&#xff0c;由项目管理协会&#xff08;PMI&#xff09;指定。本书详细介绍了项目管理的5个过程组&#xff0c;并对项目管理的10个知识领域进行了阐述。 就在2024.9.30昨天的时候中国国际人才交流基金会公布了&#xff…

崖山数据库的共享集群机制初探

本文作者&#xff1a;YashanDB高级服务工程师周国超 YashanDB共享集群是崖⼭数据库系统&#xff08;YashanDB&#xff09;的⼀个关键特性&#xff0c;它是⼀个单库多实例的多活数据库系统。⽤⼾可以连接到任意实例访问同⼀个数据库&#xff0c;多个数据库实例能够并发读写同⼀…

QT对QBytearray的data()指针进行结构体转换时会自动字节对齐填充

1、测试代码 #include <QCoreApplication>#pragma pack(push, 1) typedef struct {int a;float b;char c;int *d; }testStruct; #pragma pack(pop)#include <QByteArray> #include <QDebug>int main() {testStruct structA;structA.a 1;structA.b 2;struc…

正点原子阿波罗STM32F429IGT6移植zephyr rtos(二)---使用I2C驱动MPU6050

硬件平台&#xff1a;正点原子阿波罗STM32F429IGT6 zephyr版本&#xff1a;Zephyr version 3.7.99 开发环境&#xff1a;ubuntu 24.4 zephyr驱动开发与之前接触到的开发方式可能都不一样&#xff0c;更像是linux驱动开发&#xff0c;zephyr源码里边其实已经有写好的I2C和MPU60…

谷歌SEO:有心栽花花不开,无心插柳柳成荫!

之前一开始是想搞个谷歌SEO免费的技术教程博客&#xff08;https://www.c-sz.com/&#xff09;主要是很多时候遇到在谷歌独立站推广群里的朋友需要咨询和学习一些谷歌技术基础知识&#xff0c;当然我自己也有点小心思&#xff0c;就是希望在谷歌能吸引部分的谷歌SEO爱好者尤其包…

【AI学习】DDPM 无条件去噪扩散概率模型实现(pytorch)

这里主要使用pytorch实现基本的无条件去噪扩散模型&#xff0c;理论上面的推导这里不重点介绍。 原文理论参考&#xff1a; 前向和反向过程示意图 前向过程和后向过程 扩散过程包括正向过程和反向过程。前向过程是基于噪声调度的预定马尔可夫链。噪声表是一组方差 &#xff0…

uniapp小程序原始tabbar添加红点以及信息的方法

如图所示 很多人不知道在uniapp 小程序原始的tabbar上添加红点和红点内的信息有官方的api 从而用自定义的tabbar来做 虽然两种方法都能实现效果&#xff0c;但明显使用自带的更方便 还不如自定义

集合论(ZFC)之 序数(Ordinals) 注解

两个同构&#xff08;isomorphic&#xff09;的良序集&#xff08;Well-Ordered Set&#xff09;&#xff0c;拥有同样的序型&#xff08;Order-Type&#xff09;&#xff0c;那么序数&#xff08;Ordinal&#xff09;就是指良序集的序型&#xff08;Order-Type&#xff09;。 …

React 生命周期 - useEffect 介绍

在 React 中&#xff0c;useEffect 钩子可以被看作是函数组件中的一种副作用管理工具&#xff0c;它的行为可以模拟类组件中的不同生命周期方法。useEffect 的执行时机取决于其依赖项数组&#xff08;第二个参数&#xff09;的设置方式。 根据 useEffect 的使用方式&#xff0c…

在校大学生想从事网络安全工程师,来听听过来人的经验,你会少走很多弯路_学会大学的专业课之后可以去网络安全嘛

大家好&#xff01;一直以来都有一些大学生粉丝私信向我“取经”&#xff0c;看得出很多人对前路多多少少都有些迷茫。 因此&#xff0c;我将大家的问题整理了一下&#xff0c;主要有这几点&#xff1a; 1.国内网安工程师薪资水平&#xff1f; 2.网安行业真实前景&#xff1f;…

怎么提取视频里的音频?非常简单的提取音频方法

怎么提取视频里的音频&#xff1f;在现代数字媒体环境中&#xff0c;视频和音频的结合已成为信息传播和创作的重要手段。随着互联网的发展&#xff0c;视频内容日益丰富&#xff0c;从社交媒体短视频到在线课程&#xff0c;再到电影和纪录片&#xff0c;音频在这些内容中的角色…

全解析:如何评估PLM系统的性价比?

在当今竞争激烈的市场环境中&#xff0c;企业为了提升产品创新能力、优化生产流程、提高市场响应速度&#xff0c;纷纷引入PLM产品生命周期管理系统。然而&#xff0c;面对市场上琳琅满目的PLM系统&#xff0c;如何评估其性价比&#xff0c;成为企业决策的重要课题。本文将从多…

高清视频格式转换软件 豌豆狐 WonderFox HD Video Converter v27.7.0 中文授权版

WonderFox HD Video Converter Factory Pro 是一款来自国外团队开发的视频编辑和转换软件。它的强大之处在于支持数十种视频格式和设备专用格式之间的互相转换&#xff0c;甚至可以处理HD超清和4K极清视频&#xff01;开启显卡加速后&#xff0c;转换速度飞快&#xff0c;效率超…

LeetCode讲解篇之3. 无重复字符的最长子串

文章目录 题目描述题解思路代码实现 题目描述 题解思路 因为我们需要求无重复字符的最长子串&#xff0c;这个我们首先需要想到使用滑动窗口&#xff0c;窗口内记录无重复的子串的所有字符&#xff0c;移动窗口的右边界时&#xff0c;发现当前字符在窗口内已经出现&#xff0c…

【LeetCode】动态规划—120. 三角形最小路径和(附完整Python/C++代码)

动态规划—120. 三角形最小路径和 前言题目描述基本思路1. 问题定义2. 理解问题和递推关系3. 解决方法3.1 动态规划方法3.2 空间优化的动态规划 4. 进一步优化5. 小总结 代码实现PythonPython代码实现Python 代码解释 CC代码实现C 代码解释 总结: 前言 三角形最小路径和 是动态…

CHI协议中的LPID

总目录&#xff1a; CHI协议简读汇总-CSDN博客https://blog.csdn.net/zhangshangjie1/article/details/131877216 当某个requester包含多个logically separate processing agent时&#xff0c;使用这个LPID&#xff1b; LPID在如下的opcode下&#xff0c;取值需要正确&#…

无人机在抗洪方面的作用!

一、实时监测与灾情评估 无人机能够迅速抵达受灾区域上空&#xff0c;通过搭载的高清摄像头、红外热成像仪等传感器&#xff0c;实时传输灾区图像和视频&#xff0c;为救援指挥中心提供第一手资料。有助于快速了解灾情&#xff0c;从而制定科学合理的救援方案。 二、搜救定位…

SpringMVC源码-AbstractHandlerMethodMapping处理器映射器将@Controller修饰类方法存储到处理器映射器

SpringMVC九大内置组件之HandlerMapping处理器映射器-AbstractHandlerMethodMapping类以及子类RequestMappingHandlerMapping如何将Controller修饰的注解类以及类下被注解RequestMapping修饰的方法存储到处理器映射器中。 从RequestMappingHandlerMapping寻找: AbstractHandle…

②三菱Modbus主站MELSEC转ModbusRTU/ASCII工业MELSEC网关串口服务

三菱Modbus主站MELSEC转ModbusRTU/ASCII工业MELSEC网关串口服务https://item.taobao.com/item.htm?ftt&id834634632647 MELSEC 通信单元 MELSEC 转 RS485 &#xff08;接上一章&#xff09; 动作指示灯 电源指示灯(PWR) 表示 MS-A1-80X1 通讯模块是否处于通电中。…