【每日一题Day232】LC2699修改图中的边权 |最短路径

news/2024/12/22 14:31:54/

修改图中的边权【LC2699】

给你一个 n 个节点的 无向带权连通 图,节点编号为 0n - 1 ,再给你一个整数数组 edges ,其中 edges[i] = [ai, bi, wi] 表示节点 aibi 之间有一条边权为 wi 的边。

部分边的边权为 -1wi = -1),其他边的边权都为 数(wi > 0)。

你需要将所有边权为 -1 的边都修改为范围 [1, 2 * 109] 中的 正整数 ,使得从节点 source 到节点 destination最短距离 为整数 target 。如果有 多种 修改方案可以使 sourcedestination 之间的最短距离等于 target ,你可以返回任意一种方案。

如果存在使 sourcedestination 最短距离为 target 的方案,请你按任意顺序返回包含所有边的数组(包括未修改边权的边)。如果不存在这样的方案,请你返回一个 空数组

**注意:**你不能修改一开始边权为正数的边。

这两天为啥那么难

  • 思路

    • 首先题意要求最短路径为target,如果 ⌈ \lceil 不包含-1的边时最短距离小于target ⌋ \rfloor 或者 ⌈ \lceil 当所有-1的边修改为1时最短距离大于target ⌋ \rfloor ,那么此时无解

    • 通过两次Dijkstra 算法判断是否有合法路径存在,以及如果存在边值为多少

      设第一次/第二次Dijkstra 算法时起点至点 i i i的距离分别为 d i , 0 , d i , 1 d_{i,0},d_{i,1} di,0,di,1

      • 第一次:将所有-1边的值改为1,通过Dijkstra 算法找到起点至终点的最短距离,如果大于target,那么无解,直接返回空数组;否则进行第二次最短路径搜索。

      • 第二次:通过第一次的结果可知,我们可以增大某一条-1的边使最短路径增大至target,我们需要按照某一种顺序修改边值,并且修改后不会使之后的路径值更小,否则会影响最终的最短路径。那么可以再一次使用Dijkstra 算法,保证每次拿到的点的最短路就是最终的最短路。

        当遇到一条可以修改边值的边 x − y x-y xy时,假设修改后的值为 w w w,那么我们需要达到的目标使路径 s o u r c e − X − Y − d e s t i n a t i o n source-X-Y-destination sourceXYdestination的路径值为target
        d x , 0 + w + d d e s t i n a t i o n , 0 − d y , 0 = t a r g e t d_{x,0}+w+d_{destination,0}-d_{y,0}=target dx,0+w+ddestination,0dy,0=target
        那么w为
        t a r g e t − d x , 0 − d d e s t i n a t i o n , 0 + d y , 0 target-d_{x,0}-d_{destination,0}+d_{y,0} targetdx,0ddestination,0+dy,0
        此时还存在一种情况:如果修改完所有的值,最短路径仍小于target,此时也返回空数组

  • 实现

    • 由于最后需要更改数组中的权值,因此构建邻接数组时需要记录边的下标
    • 第二次Dijkstra 算法时,当修改不在最短路径上的边 X − Y X-Y XY的权值时,不会对答案产生影响
    • 最后可能会有部分边权值为-1,因为第一次Dijkstra 算法时没有将数组中的值改为1,或者有些边不影响最短路,把这些边都改成 1 就行。
    class Solution {public int[][] modifiedGraphEdges(int n, int[][] edges, int source, int destination, int target) {List<int[]> g[] = new ArrayList[n];Arrays.setAll(g, e -> new ArrayList<>());for (int i = 0; i < edges.length; i++) {int x = edges[i][0], y = edges[i][1];g[x].add(new int[]{y, i});g[y].add(new int[]{x, i}); // 建图,额外记录边的编号}var dis = new int[n][2];for (int i = 0; i < n; i++)if (i != source)dis[i][0] = dis[i][1] = Integer.MAX_VALUE;dijkstra(g, edges, destination, dis, 0, 0);int delta = target - dis[destination][0];if (delta < 0) // -1 全改为 1 时,最短路比 target 还大return new int[][]{};dijkstra(g, edges, destination, dis, delta, 1);if (dis[destination][1] < target) // 最短路无法再变大,无法达到 targetreturn new int[][]{};for (var e : edges)if (e[2] == -1) // 剩余没修改的边全部改成 1e[2] = 1;return edges;}// 朴素 Dijkstra 算法// 这里 k 表示第一次/第二次private void dijkstra(List<int[]> g[], int[][] edges, int destination, int[][] dis, int delta, int k) {int n = g.length;var vis = new boolean[n];for (; ; ) {// 找到当前最短路,去更新它的邻居的最短路// 根据数学归纳法,dis[x][k] 一定是最短路长度int x = -1;for (int i = 0; i < n; ++i)if (!vis[i] && (x < 0 || dis[i][k] < dis[x][k]))x = i;if (x == destination) // 起点 source 到终点 destination 的最短路已确定return;vis[x] = true; // 标记,在后续的循环中无需反复更新 x 到其余点的最短路长度for (var e : g[x]) {int y = e[0], eid = e[1];int wt = edges[eid][2];if (wt == -1)wt = 1; // -1 改成 1if (k == 1 && edges[eid][2] == -1) {// 第二次 Dijkstra,改成 wint w = delta + dis[y][0] - dis[x][1];if (w > wt)edges[eid][2] = wt = w; // 直接在 edges 上修改}// 更新最短路dis[y][k] = Math.min(dis[y][k], dis[x][k] + wt);}}}
    }作者:灵茶山艾府
    链接:https://leetcode.cn/problems/modify-graph-edge-weights/solutions/2278296/xiang-xi-fen-xi-liang-ci-dijkstrachou-mi-gv1m/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    
    • 复杂度
      • 时间复杂度: O ( n 2 ) \mathcal{O}(n^2) O(n2),在稠密图(本题最坏情况)中,算法的时间复杂度与边的数量 O ( n 2 ) \mathcal{O}(n^2) O(n2)成正比。
      • 空间复杂度: O ( m ) \mathcal{O}(m) O(m)

http://www.ppmy.cn/news/345452.html

相关文章

(数组) 922. 按奇偶排序数组 II ——【Leetcode每日一题】

❓922. 按奇偶排序数组 II 难度&#xff1a;简单 给定一个非负整数数组 nums&#xff0c; nums 中一半整数是 奇数 &#xff0c;一半整数是 偶数 。 对数组进行排序&#xff0c;以便当 nums[i] 为奇数时&#xff0c;i 也是 奇数 &#xff1b;当 nums[i] 为偶数时&#xff0c…

【Selenium2+python】自动化unittest生成测试报告

前言 批量执行完用例后&#xff0c;生成的测试报告是文本形式的&#xff0c;不够直观&#xff0c;为了更好的展示测试报告&#xff0c;最好是生成HTML格式的。 unittest里面是不能生成html格式报告的&#xff0c;需要导入一个第三方的模块&#xff1a;HTMLTestRunner 一、导…

python_概率密度图

amtpd.read_excel(ramt.xlsx)import seaborn as sns sns.kdeplot(amt.limit_final_yxh)

HP DA1023电脑 Hackintosh 黑苹果efi引导文件

原文来源于黑果魏叔官网&#xff0c;转载需注明出处。&#xff08;下载请直接百度黑果魏叔&#xff09; 硬件配置 硬件型号驱动情况 主板HP DA1023 处理器Intel(R) Core(TM) i5-8265U已驱动 内存8 GB 2400 MHz DDR4.已驱动 硬盘Samsung SSD 980 250GB(macOS)已驱动 显卡I…

红米note_标注2013122_官方线刷包_救砖包_解账户锁

红米NOTE移动增强版2013122下载地址&#xff1a; https://pan.baidu.com/s/1NWD3kq_mnlInROsext2YYw 刷机包平台驱动教程&#xff0c;全部打包在一起 下载解压后&#xff0c;按照刷机教程进行刷机

红米二代竟有四款!64位/八核/5.5英寸

除了2014017和2014018两款新机之外&#xff0c;我们还在工信部网站上发现了小米的另外两款新机&#xff0c;分别是2013121和2014122&#xff0c;其外观和前者保持一致&#xff0c;因此应该也是红米家族的两款产品。 具体配置方面&#xff0c;2013121机身尺寸为15478.79.45mm&am…

011 - STM32学习笔记 - 串口通讯

011 - STM32学习笔记 - 串口通讯 关于串口的相关概念各位可以在网上查一下相关介绍&#xff0c;这里直接开始学习STM32上的串口配置和通讯测试了 在学习相关寄存器之前&#xff0c;先看一下USART的功能框图 1、USART引脚 引脚名称引脚功能TX数据发送端RX数据接收端SW_RX单线…

php安装拓展之phpize方式安装

php的源码包中有一个ext文件夹里面好多拓展插件&#xff0c;如果编译安装php的时候&#xff0c;没有安装拓展&#xff0c;可以后续通过phpize安装拓展 进入 ext文件夹 之后假如我需要安装imap插件 cd imap生成 configure文件 /usr/local/php/bin/phpize直接在imap目录执行 p…