细分图中的可到达节点 : 常规最短路运用题

news2025/2/27 8:15:17

题目描述

Tag : 「最短路」、「单源最短路」、「Dijkstra」、「SPFA」

给你一个无向图(原始图),图中有 n 个节点,编号从 0 到 n - 1 。你决定将图中的每条边 细分 为一条节点链,每条边之间的新节点数各不相同。

图用由边组成的二维数组 edges 表示,其中 edges[i] = [u_{i}, v_{i}, cnt_{i}]edges[i]=[ui​,vi​,cnti​] 表示原始图中节点 u_{i}ui​ 和 v_{i}vi​ 之间存在一条边,cnt_{i}cnti​ 是将边 细分 后的新节点总数。注意,cnt_{i} = 0cnti​=0 表示边不可细分。

要 细分边 [u_{i}, v_{i}][ui​,vi​] ,需要将其替换为 (cnt_{i} + 1)(cnti​+1) 条新边,和 cnt_{i}cnti​ 个新节点。新节点为 x_1, x_2, ..., x_{cnt_{i}}x1​,x2​,...,xcnti​​ ,新边为 [u_{i}, x_{1}], [x_{1}, x_{2}], [x_{2}, x_{3}], ..., [x_{cnt_{i}}+1, x_{cnt_{i}}], [x_{cnt_{i}}, v_{i}][ui​,x1​],[x1​,x2​],[x2​,x3​],...,[xcnti​​+1,xcnti​​],[xcnti​​,vi​] 。

现在得到一个 新的细分图 ,请你计算从节点 0 出发,可以到达多少个节点?如果节点间距离是 maxMoves 或更少,则视为 可以到达 。

给你原始图和 maxMoves ,返回 新的细分图中从节点 0 出发 可到达的节点数 。

示例 1:

输入:edges = [[0,1,10],[0,2,1],[1,2,2]], maxMoves = 6, n = 3

输出:13

解释:边的细分情况如上图所示。
可以到达的节点已经用黄色标注出来。
复制代码

示例 2:

输入:edges = [[0,1,4],[1,2,6],[0,2,8],[1,3,1]], maxMoves = 10, n = 4

输出:23
复制代码

示例 3:

输入:edges = [[1,2,4],[1,4,5],[1,3,1],[2,3,4],[3,4,5]], maxMoves = 17, n = 5

输出:1

解释:节点 0 与图的其余部分没有连通,所以只有节点 0 可以到达。
复制代码

提示:

  • 0 <= edges.length <= \min(n * (n - 1) / 2, 10^4)0<=edges.length<=min(n∗(n−1)/2,104)
  • edges[i].length = 3edges[i].length=3
  • 0 <= u_{i} < v_{i} < n0<=ui​<vi​<n
  • 图中 不存在平行边
  • 0 <= cnt_{i} <= 10^40<=cnti​<=104
  • 0 <= maxMoves <= 10^90<=maxMoves<=109
  • 1 <= n <= 30001<=n<=3000

朴素 Dijkstra

为了方便,我们将原始图边的数量记为 m,因此对于原始图而言,点的数量 30003000,边的数量为 1000010000。

题目要我们求新图上,从 0 点出发可到达的点的数量,我们将原图上存在的点称为「原点」,细分边上增加的点称为「细分点」,两类点中可达点的数量即是答案。

在分别考虑如何统计两类点之前,我们需要重新定义一下边的权重:若原点 u 和原点 v 的边上存在 c 个细分点,我们将原点 u 和原点 v 之间的边看作是一条权重为 c + 1 的无向边(结合题意,c 个点存在 c + 1 个分段/距离)

重新定义边的权重后,因为该图是「稠密图」,我们可以使用「朴素 Dijkstra」来求解最短路,得到 distdist 数组,其中 dist[x] = tdist[x]=t 含义为从原点 0 点出发,到达原点 x 的最短距离为 t

不了解最短路的同学可以看前置 🧀 : 涵盖所有的「存图方式」与「最短路算法(详尽注释)」

随后考虑如何统计答案(可达点的数量),根据统计点的类型分情况讨论:

  1. 对于原点:若有 dist[x] < maxdist[x]<max 的话,说明原点 x 可达,累加到答案中;

  2. 对于细分点:由于所有的细分点都在原图边上,因此我们可以统计所有原图边上有多少细分点可达。 对于任意一条边 e(u, v)e(u,v) 而言,该边上可达点数量包含「经过原点 u 可达」以及「经过原点 v 可达」的交集,其中原点 0 到达原点 u 以及原点 v 的距离,我们是已知的。因此经过原点 u 可达的数量为 \max(0, max - dist[u])max(0,max−dist[u]),经过原点 v 可达的数量为 \max(0, max - dist[v])max(0,max−dist[v]),两者之和与该边上细分点的总数取 min 即是这条边可达点的数量。

代码:

class Solution {
    static int N = 3010, INF = 0x3f3f3f3f;
    static int[][] g = new int[N][N];
    static int[] dist = new int[N];
    static boolean[] vis = new boolean[N];
    public int reachableNodes(int[][] edges, int max, int n) {
        // 建图
        for (int i = 0; i < n; i++) Arrays.fill(g[i], INF);
        for (int[] info : edges) {
            int a = info[0], b = info[1], c = info[2] + 1;
            g[a][b] = g[b][a] = c;
        }
        // 朴素 Dijkstra
        Arrays.fill(dist, INF);
        Arrays.fill(vis, false);
        dist[0] = 0;
        for (int i = 0; i < n; i++) {
            int t = -1;
            for (int j = 0; j < n; j++) {
                if (!vis[j] && (t == -1 || dist[j] < dist[t])) t = j;
            }
            vis[t] = true;
            for (int j = 0; j < n; j++) dist[j] = Math.min(dist[j], dist[t] + g[t][j]);
        }
        // 统计答案
        int ans = 0;
        for (int i = 0; i < n; i++) {
            if (dist[i] <= max) ans++;
        }
        for (int[] info : edges) {
            int a = info[0], b = info[1], c = info[2];
            int c1 = Math.max(0, max - dist[a]), c2 = Math.max(0, max - dist[b]);
            ans += Math.min(c, c1 + c2);
        }
        return ans;
    }
}
复制代码
  • 时间复杂度:建图复杂度为 O(m)O(m);使用朴素 Dijkstra 求最短路复杂度为 O(n^2)O(n2);统计答案复杂度为 O(n + m)O(n+m)。整体复杂度为 O(m + n^2)O(m+n2)
  • 空间复杂度:O(n^2)O(n2)

SPFA

从数据范围来看,无论是朴素 Dijkstra 还是堆优化版的 Dijkstra 都可以过,复杂度分别为 O(n^2)O(n2) 和 O(m\log{n})O(mlogn)。

那 Bellman Ford 类的单源最短路就无法通过了吗?

理论上,无论是 Bellman Ford 还是 SPFA 复杂度均为 O(n \times m)O(n×m),均无法通过本题。但实际上 SPFA 由于使用队列对松弛顺序进行了调整,因此在应对「非菊花图」时均表现良好,复杂度可视为 O(k \times m)O(k×m),近似 O(m)O(m)。

代码:

class Solution {
    static int N = 3010, M = 20010, INF = 0x3f3f3f3f, idx = 0;
    static int[] he = new int[N], e = new int[M], ne = new int[M], w = new int[M];
    static int[] dist = new int[N];
    static boolean[] vis = new boolean[N];
    void add(int a, int b, int c) {
        e[idx] = b;
        ne[idx] = he[a];
        w[idx] = c;
        he[a] = idx++;
    }
    public int reachableNodes(int[][] edges, int max, int n) {
        // 建图
        Arrays.fill(he, -1);
        idx = 0;
        for (int[] info : edges) {
            int a = info[0], b = info[1], c = info[2] + 1;
            add(a, b, c); add(b, a, c);
        }
        // SPFA
        Arrays.fill(dist, INF);
        Arrays.fill(vis, false);
        Deque<Integer> d = new ArrayDeque<>();
        d.addLast(0);
        dist[0] = 0;
        vis[0] = true;
        while (!d.isEmpty()) {
            int t = d.pollFirst();
            vis[t] = false;
            for (int i = he[t]; i != -1; i = ne[i]) {
                int j = e[i];
                if (dist[j] > dist[t] + w[i]) {
                    dist[j] = dist[t] + w[i];
                    if (vis[j]) continue;
                    d.addLast(j);
                    vis[j] = true;
                }
            }
        }
        // 统计答案
        int ans = 0;
        for (int i = 0; i < n; i++) {
            if (dist[i] <= max) ans++;
        }
        for (int[] info : edges) {
            int a = info[0], b = info[1], c = info[2];
            int c1 = Math.max(0, max - dist[a]), c2 = Math.max(0, max - dist[b]);
            ans += Math.min(c, c1 + c2);
        }
        return ans;
    }
}
复制代码
  • 时间复杂度:建图复杂度为 O(m)O(m);使用 SPFA 求最短路复杂度为 O(n \times m)O(n×m);统计答案复杂度为 O(n + m)O(n+m)。整体复杂度为 O(n \times m)O(n×m)
  • 空间复杂度:O(n + m)O(n+m)

最后

这是我们「刷穿 LeetCode」系列文章的第 No.882 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。

在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。

为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:github.com/SharingSour… 。

在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。

 

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

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

相关文章

[前端框架]-VUE(上篇)

Vue (读音 /vjuː/&#xff0c;类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是&#xff0c;Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层&#xff0c;不仅易于上手&#xff0c;还便于与第三方库或既有项目整合。另一方面&#xff0…

链表经典算法题目

1.回文链表 编写一个函数&#xff0c;检查输入的链表是否是回文的。 示例 1&#xff1a; 输入&#xff1a; 1->2 输出&#xff1a; false 示例 2&#xff1a; 输入&#xff1a; 1->2->2->1 输出&#xff1a; true 笔试的写法 重点是快速code,不考虑空间复杂度…

蒙特卡洛法(Monte Carlo)电动汽车负荷预测matlab程序设计

电动汽车充电负荷的时间分布预测 规模化电动汽车充电负荷在未来某一天随时间特性的分布规律是研究电动汽车发展对配 电网影响以及充电站选址定容问题的前提与基础。电动汽车充电负荷的分布情况与车主的行 为特征有关&#xff0c;不同类型的电动汽车车主出行规律以及充电习惯不…

<Linux系统复习>信号

一、本章重点 1、什么是信号&#xff1f; 2、查看信号列表 3、信号捕捉 4、信号产生的5种方式 5、介绍CoreDump 6、信号处理的方式 7、如何理解信号产生到处理的过程 8、sigpending、sigprocmask、sigaction函数的使用 9、信号处理的时机 10、SIGCHLD信号 11、可重入函数 01 什…

Codeforces Round 836 (Div. 2) A - C

A:SSeeeeiinngg DDoouubbllee 题意&#xff1a;给定一个字符串&#xff0c;每个字符串的字符可以出现两次&#xff0c;要求通过重新排列构造一个回文串。 思路&#xff1a;直接暴力可以&#xff0c;每个字符头部一个尾部一个。 #include<cstdio> #include <iostream…

不使用实体类的情况下接收SQL查询结果、@Autowired注入为null解决

目录 一、场景 二、环境 三、使用 1、数据库表以及数据准备 2、项目导入必要依赖 3、添加连接数据库配置文件 4、编写测试方法 5、执行结果 四、将SQL单独提取出来 2.1 定义查询接口方法 2.2 测试 2.3 测试结果 五、问题记录&#xff1a; Autowired注入失败/null的…

b、B、KB、Kib、MB、MiB、GB、GiB、TB、TiB的区别

1024这个数字&#xff0c;想必计算机行业从业人员应该不会陌生&#xff0c;甚至10月24日还被当做程序员日&#xff0c;如果你问一个程序员1GB等于多少MB,他大概率会不假思索回答:1024。 没错&#xff0c;对于稍微对计算机或者网络有了解的人&#xff0c;一般都认为1024是数据容…

最短路算法 - dijkstra

最短路算法 - dijkstra1. 算法介绍2. 实战2.1 Reachable Nodes In Subdivided Graph3 参考1. 算法介绍 算法目的&#xff1a;求图中某点 s 到其余各点的最短距离 算法步骤&#xff1a; 初始化距离数组 dis 和优先级队列&#xff0c;其中 dis[i] 表示 s 点到当前 i 点的最短距…

树莓派上搭建SVN服务器

目录 一、服务端安装步骤 1.安装svn 2.创建目录 3.创建版本仓库 4.修改配置&#xff08;authz,passwd,svnserve.conf&#xff09; 5.启动服务 二、tortoisSVN客户端安装 三、结束 一、服务端安装步骤 1.安装svn sudo apt-get install subversion 2.创建目录 sudo m…

品RocketMQ 源码,学习并发编程三大神器

这篇文章&#xff0c;笔者结合 RocketMQ 源码&#xff0c;分享并发编程三大神器的相关知识点。 1 CountDownLatch 实现网络同步请求 CountDownLatch 是一个同步工具类&#xff0c;用来协调多个线程之间的同步&#xff0c;它能够使一个线程在等待另外一些线程完成各自工作之后&…

selenium--获取页面信息和截图

获取页面信息namecurrent_urltitlecurrent_window_handlewindow_handlespage_source简单用法—— 判断页面截图1.get_screenshot_as_png2.get_screenshot_as_file获取页面信息 主要方法如下图&#xff1a; 介绍一下常用的方法&#xff1a; name 获取浏览器名字 current_u…

Packet Tracer 实验 - 排除多区域 OSPFv3 故障

地址分配表 设备 接口 IPv6 全局单播地址 IPv6 本地链路地址 默认网关 ISP GigabitEthernet0/0 2001:DB8:C1:1::1/64 FE80::C1 不适用 ASBR GigabitEthernet0/0 2001:DB8:C1:1::2/64 FE80::7 不适用 Serial0/0/0 2001:DB8:A8EA:F0A::1 FE80::7 不适用 S…

如何通过 kubectl 进入 node shell

概述 假设这样一个场景&#xff1a; 生产环境中&#xff0c;Node 都需要通过堡垒机登录&#xff0c;但是 kubectl 是可以直接在个人电脑上登录的。 这种场景下&#xff0c;我想要通过 kubectl 登录到 K8S 集群里的 Node&#xff0c;可以实现吗&#xff1f; 可以的&#xff…

LinkedList与链表

目录 1.链表 2.链表的模拟实现 3.LinkedList的模拟实现 4.LinkedList的使用 4.1 什么是LinkedList 4.2 LinkedList的使用 5.ArrayList和LinkedList的区别 我的GitHub&#xff1a;Powerveil GitHub 我的Gitee&#xff1a;Powercs12 (powercs12) - Gitee.com 皮卡丘每天学…

机器人十大前沿热点领域(2022-2023)

中国电子学会嵌入式系统与机器人分会主任委员、北京航空航天大学机器人研究所名誉所长、中关村智友研究院院长王田苗博士出席大会闭幕式&#xff0c;并现场发布《机器人十大前沿热点领域(2022-2023)》。归纳出2022-2023年机器人十大前沿技术&#xff1b;并结合我国国情和机器人…

自动化项目倍加福测距仪QSM WCS RS485 与西门子S7 200通信

1、程序流程图 2、WCS位置数据处理流程 第一步&#xff1a;设置S7-200的RS485的通讯波特率19.2kbps&#xff0c;通讯格式&#xff08;8&#xff0c;1&#xff0c;E&#xff09;&#xff1b; 第二步&#xff1a;PLC向WCS发送请求码&#xff1a; A0A1为0&#xff0c;表示读码器地…

现代密码学导论-16-选择明文攻击和CPA安全

目录 PCA不可区分实验 DEFINITION 3.21 PCA安全的加密方案 LR预言机实验 DEFINITION 3.22 多明文PCA安全的加密方案 THEOREM 3.23 定义3.21和定义3.22等价 PCA不可区分实验 通过运行G(1^n)获得密钥k敌手A被给定输入1^n并拥有访问预言机Enck()的权利&#xff0c;敌手A输出一…

想要精通算法和SQL的成长之路 - 无重叠区间

想要精通算法和SQL的成长之路 - 无重叠区间前言一. 无重叠区间前言 想要精通算法和SQL的成长之路 - 系列导航 一. 无重叠区间 原题链接 给定一个区间的集合 intervals &#xff0c;其中 intervals[i] [starti, endi] 。返回 需要移除区间的最小数量&#xff0c;使剩余区间互…

神经网络和深度学习-处理多维特征的输入

处理多维特征的输入 前面有两个数据集&#xff0c;一个回归&#xff0c;一个分类。 在回归中输出y属于实数&#xff0c;而在分类中输出y属于一个离散的集合 例如在糖尿病分类的数据集中Diabetes Dataset&#xff0c;每一行作为一个sample&#xff08;样本&#xff09;&#x…

Node.js 入门教程 11 Node.js 从命令行接收参数

Node.js 入门教程 Node.js官方入门教程 Node.js中文网 本文仅用于学习记录&#xff0c;不存在任何商业用途&#xff0c;如侵删 文章目录Node.js 入门教程11 Node.js 从命令行接收参数11 Node.js 从命令行接收参数 当使用以下命令调用 Node.js 应用程序时&#xff0c;可以传入任…