LeetCode 1631. Path With Minimum Effort【最小瓶頸路;二分+BFS或DFS;計數排序+并查集;最小生成樹】1947

本文屬于「征服LeetCode」系列文章之一,這一系列正式開始于2021/08/12。由于LeetCode上部分題目有鎖,本系列將至少持續到刷完所有無鎖題之日為止;由于LeetCode還在不斷地創建新題,本系列的終止日期可能是永遠。在這一系列刷題文章中,我不僅會講解多種解題思路及其優化,還會用多種編程語言實現題解,涉及到通用解法時更將歸納總結出相應的算法模板。

為了方便在PC上運行調試、分享代碼文件,我還建立了相關的倉庫:https://github.com/memcpy0/LeetCode-Conquest。在這一倉庫中,你不僅可以看到LeetCode原題鏈接、題解代碼、題解文章鏈接、同類題目歸納、通用解法總結等,還可以看到原題出現頻率和相關企業等重要信息。如果有其他優選題解,還可以一同分享給他人。

由于本系列文章的內容隨時可能發生更新變動,歡迎關注和收藏征服LeetCode系列文章目錄一文以作備忘。

你準備參加一場遠足活動。給你一個二維?rows x columns?的地圖?heights?,其中?heights[row][col]?表示格子?(row, col)?的高度。一開始你在最左上角的格子?(0, 0)?,且你希望去最右下角的格子?(rows-1, columns-1)?(注意下標從?0?開始編號)。你每次可以往??四個方向之一移動,你想要找到耗費?體力?最小的一條路徑。

一條路徑耗費的?體力值?是路徑上相鄰格子之間?高度差絕對值?的?最大值?決定的。

請你返回從左上角走到右下角的最小?體力消耗值?。

示例 1:

輸入:heights = [[1,2,2],[3,8,2],[5,3,5]]
輸出:2
解釋:路徑 [1,3,5,3,5] 連續格子的差值絕對值最大為 2 。
這條路徑比路徑 [1,2,2,2,5] 更優,因為另一條路徑差值最大值為 3

示例 2:

輸入:heights = [[1,2,3],[3,8,4],[5,3,5]]
輸出:1
解釋:路徑 [1,2,3,4,5] 的相鄰格子差值絕對值最大為 1 ,比路徑 [1,3,5,3,5] 更優。

示例 3:

輸入:heights = [[1,2,1,1,1],[1,2,1,2,1],[1,2,1,2,1],[1,2,1,2,1],[1,1,1,2,1]]
輸出:0
解釋:上圖所示路徑不需要消耗任何體力。

提示:

  • rows == heights.length
  • columns == heights[i].length
  • 1 <= rows, columns <= 100
  • 1 <= heights[i][j] <= 10^6

同類題目:

  • LeetCode 2812. Find the Safest Path in a Grid
  • LeetCode 778. Swim in Rising Water
  • LeetCode 1631. 最小體力消耗路徑

這幾道題,雖然物理問題不同,但是背后的數學模型幾乎完全相同,唯一的區別在于:

  • 第 1631 題將相鄰格子的高度差作為邊的權重,是找最小化的 相鄰格子最大絕對高度差。
  • 第 778 題將相鄰兩格子間高度的最大值作為邊的權重,是找最小化的 最大高度
  • 第 2812 題將當前方格到最近小偷的距離作為邊的權重,是找最大化的 最小距離

無向圖 G G G x x x y y y最小瓶頸路是這樣的一類簡單路徑,滿足這條路徑上的最大邊權在所有 x x x y y y 的簡單路徑中是最小的。
根據最小生成樹定義, x x x y y y 的最小瓶頸路上的最大邊權等于最小生成樹上 x x x y y y 路徑上的最大邊權。雖然最小生成樹不唯一,但是每種最小生成樹 x x x y y y 路徑的最大邊權相同且為最小值。也就是說,每種最小生成樹上的 x x x y y y 的路徑均為最小瓶頸路。

無向圖 G G G x x x y y y最大瓶頸路是這樣的一類簡單路徑,滿足這條路徑上的最小邊權在所有 x x x y y y 的簡單路徑中是最大的。
根據最小生成樹定義, x x x y y y 的最大瓶頸路上的最小邊權等于最大生成樹上 x x x y y y 路徑上的最小邊權。雖然最大生成樹不唯一,但是每種最大生成樹 x x x y y y 路徑的最小邊權相同且為最大值。也就是說,每種最大生成樹上的 x x x y y y 的路徑均為最大瓶頸路。

我們不必實際求出整個最小/大生成樹,只需要求出最小/大生成樹上 x x x y y y 路徑上的最大/小邊權

在看清楚物理問題背后的數學模型后,這幾道題都會迎刃而解。

解法1 二分+BFS/DFS

這種做法是LeetCode 2812. Find the Safest Path in a Grid的完全簡化版、在2812中需要先進行一次多源BFS、再來二分答案,也用在LeetCode 1631. 最小體力消耗路徑中。

根據題目中的描述,給定了 h e i g h t s [ i ] [ j ] heights[i][j] heights[i][j] 的范圍是 [ 1 , 1 0 6 ] [1, 10^6 ] [1,106] ,所以答案(絕對高度差)必然落在范圍 [ 0 , 1 0 6 ] [0, 10^6] [0,106]

  • 如果允許消耗的體力值 h h h 越低,網格上可以越過的部分就越少,存在從左上角到右下角的一條路徑的可能性越小
  • 如果允許消耗的體力值 h h h 越高,網格上可以游泳的部分就越多,存在從左上角到右下角的一條路徑的可能性越大。

這是本問題具有的 單調性 。因此可以使用二分查找定位到最小體力消耗值。

假設最優解為 l l l 的話(恰好能到達右下角的體力消耗),那么小于 l l l 的體力消耗無法到達右下角,大于 l l l 的體力消耗能到達右下角,因此在以最優解 l l l 為分割點的數軸上具有二段性,可以通過「二分」找到分割點 l l l

注意:「二分」的本質是兩段性,并非單調性。只要一段滿足某個性質,另外一段不滿足某個性質,就可以用「二分」。其中 33. 搜索旋轉排序數組 是一個很好的說明例子。

具體來說:在區間 [ 0 , 1 e 6 ] [0,1e6] [0,1e6] 里猜一個整數,針對這個整數從起點(左上角)開始做一次DFS或BFS:

  • 當小于等于該數值時,如果存在一條從左上角到右下角的路徑,說明答案可能是這個數值,也可能更小
  • 當小于等于該數值時,如果不存在一條從左上角到右下角的路徑,說明答案一定比這個數值更大。
  • 按照這種方式不斷縮小搜索的區間,最終找到最小體力消耗。
class Solution {
public:int minimumEffortPath(vector<vector<int>>& heights) {int n = heights.size(), m = heights[0].size();typedef pair<int, int> pii;int d[4][2] = {-1, 0, 1, 0, 0, -1, 0, 1};auto check = [&](int lim) {bool vis[n][m]; memset(vis, false, sizeof(vis));queue<pii> q;q.push(pii(0, 0)); vis[0][0] = true;while (!q.empty()) {pii p = q.front(); q.pop();int i = p.first, j = p.second;if (i == n - 1 && j == m - 1) return true;for (int k = 0; k < 4; ++k) {int x = i + d[k][0], y = j + d[k][1];if (x < 0 || x >= n || y < 0 || y >= m || abs(heights[x][y] - heights[i][j]) > lim || vis[x][y])continue;q.push(pii(x, y));vis[x][y] = true;}}return vis[n - 1][m - 1];};int l = 1, r = 1e6 + 1;while (l < r) {int mid = (l + r) >> 1;if (check(mid)) r = mid;else l = mid + 1;}return l;}
};

復雜度分析:

  • 時間復雜度: O ( M N log ? C ) O(MN \log C) O(MNlogC) 。 其中 N , M N,M N,M 是方格的邊長。最差情況下進行 log ? C ( C = 1 e 6 ) \log C\ (C = 1e6) logC?(C=1e6) 次二分查找,每一次二分查找最差情況下要遍歷所有單元格一次,時間復雜度為 O ( M N ) O(MN) O(MN)
  • 空間復雜度: O ( M N ) O(MN) O(MN) 。 數組 v i s vis vis 的大小為 M N MN MN ,如果使用深度優先遍歷,須要使用的棧的大小最多為 M N MN MN ,如果使用廣度優先遍歷,須要使用的隊列的大小最多為 M N MN MN

解法2 最小瓶頸路模型+最小生成樹(Prim+堆/Kruskal+邊集數組)

根據題意,我們要找的是從左上角到右下角的最優路徑,其中最優路徑是指途徑的邊的最大權重值最小,然后輸出最優路徑中的最大權重值。此處的邊權為途徑節點間高度差絕對值

根據最小瓶頸路模型,我們可以使用Kruskal最小生成樹算法:

  1. 先遍歷所有的點,將所有的邊加入集合,存儲的格式為數組 [ a , b , w ] [a, b, w] [a,b,w] ,代表編號為 a a a 的點和編號為 b b b 的點之間的權重為 w w w(按照題意, w w w 為兩者的高度絕對差)。
  2. 對邊集集合進行排序,按照 w w w 進行從小到達排序
  3. 當我們有了所有排好序的候選邊集合之后,對邊從前往后處理,選擇那些不會出現環的邊加入最小生成樹中。
  4. 每次加入一條邊之后,使用并查集來查詢左上角點和右下角點是否連通。如果連通,那么該邊的權重即是答案
class Solution {private class UnionFind {private int[] parent;public UnionFind(int n) {parent = new int[n];for (int i = 0; i < n; ++i) parent[i] = i;}public int find(int x) {return x != parent[x] ? parent[x] = find(parent[x]) : x;}public boolean isConnected(int x, int y) { return find(x) == find(y); }public void union(int p, int q) {int rp = find(p), rq = find(q);if (rp == rq) return;parent[rp] = rq;}}public int minimumEffortPath(int[][] grid) {int n = grid.length, m = grid[0].length;int len = n * m;int[][] d = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};UnionFind unionFind = new UnionFind(len);// 預處理所有無向邊// edge存[a,b,w]: 代表從a到b所需的時間點為w// 雖然我們可以往四個方向移動,但只要對于每個點都添加「向右」和「向下」// 兩條邊的話,其實就已經覆蓋了所有邊List<int[]> edges = new ArrayList<>();for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {int index = i * m + j;int a, b, w;if (i + 1 < n) {a = index; b = (i + 1) * m + j;w = Math.abs(grid[i][j] - grid[i + 1][j]);edges.add(new int[]{a, b, w});}if (j + 1 < m) {a = index; b = i * m + (j + 1);w = Math.abs(grid[i][j] - grid[i][j + 1]);edges.add(new int[]{a, b, w});}   }}Collections.sort(edges, (a, b) -> a[2] - b[2]); // 根據w權值升序// 從「小邊」開始添加,當某一條邊應用之后,恰好使得「起點」和「結點」聯通// 那么代表找到了「最短路徑」中的「權重最大的邊」int start = 0, end = len - 1;for (int[] edge : edges) {int a = edge[0], b = edge[1], w = edge[2];unionFind.union(a, b);if (unionFind.isConnected(start, end)) return w;}return 0;}
}

Kruskal雖然沒有求出完整最小生成樹,但是對所有邊進行了排序。我們可以使用Prim算法+優先隊列。

下面將此問題抽象為一張帶權有向圖,每個單元格為頂點,有指向相鄰頂點的有向邊,邊的權值為指向頂點的高度絕對差。然后用Prim算法,求出最小生成樹上左上角的點到右下角的點的路徑上的最大邊權值。此時不用對所有邊進行排序。

class Solution {public int minimumEffortPath(int[][] grid) {int n = grid.length, m = grid[0].length;boolean[][] vis = new boolean[n][m];// 必須將 先前加入隊列時的邊權重 加入int[]中Queue<int[]> minHeap = new PriorityQueue<>((a, b) -> a[2] - b[2]);minHeap.offer(new int[]{0, 0, 0});int[][] dist = new int[n][m];for (int[] row : dist)Arrays.fill(row, Integer.MAX_VALUE);dist[0][0] = 0;int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};while (!minHeap.isEmpty()) { // 找最短的邊int[] front = minHeap.poll();int x = front[0], y = front[1], d = front[2];if (vis[x][y]) continue;vis[x][y] = true;if (x == n - 1 && y == m - 1) break;// 更新for (int i = 0; i < 4; ++i) {int u = x + directions[i][0], v = y + directions[i][1];if (u >= 0 && u < n && v >= 0 && v < m && !vis[u][v] && // prim算法Math.max(d, Math.abs(grid[x][y] - grid[u][v]))<= dist[u][v]) {dist[u][v] = Math.max(d,Math.abs(grid[x][y] - grid[u][v]));minHeap.offer(new int[]{u, v, dist[u][v]});}}}return dist[n - 1][m - 1];}
}

復雜度分析:

  • 時間復雜度: O ( M N log ? M N ) O(MN \log MN) O(MNlogMN) 。 使用了優先隊列的 Prim 算法的時間復雜度是 O ( E log ? E ) O(E \log E) O(ElogE) ,這里 E E E 是邊數,至多是頂點數的 4 4 4 倍,頂點數為 M N MN MN ,因此 O ( E log ? E ) = O ( 4 M N log ? M N ) = O ( M N log ? M N ) O(E \log E) = O(4MN \log MN) = O(MN \log MN) O(ElogE)=O(4MNlogMN)=O(MNlogMN)
  • 空間復雜度: O ( M N ) O(MN) O(MN) 。 數組 v i s vis vis d i s t dist dist 的大小為 O ( M N ) O(MN) O(MN) ,優先隊列中保存的邊的總數也是 M N MN MN 級別的。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/35566.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/35566.shtml
英文地址,請注明出處:http://en.pswp.cn/news/35566.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

阿里云PolarDB數據庫倚天ARM架構詳細介紹

阿里云云原生數據庫PolarDB MySQL版推出倚天ARM架構&#xff0c;倚天ARM架構規格相比X86架構規格最高降價45%&#xff0c;PolarDB針對自研倚天芯片&#xff0c;從芯片到數據庫內核全鏈路優化&#xff0c;助力企業降本增效。基于阿里云自研的倚天服務器&#xff0c;同時在數據庫…

誰能講清楚Spark之Spark系統架構

### 整體架構概述 Spark與Hadoop MapReduce的結構類似,Spark也采用Master-Worker結構。如果一個Spark集群由4個節點組成,即1個Master節點和3個Worker節點,那么在部署Standalone版本后,Spark部署的系統架構圖如圖2.1所示。簡單來說,Master節點負責管理應用和任務,…

【0day】復現廣聯達-Linkworks 協同辦公管理平臺GetUserByUserCode接口存在SQL注入漏洞

目錄 一、漏洞描述 二、影響版本 三、資產測繪 四、漏洞復現 一、漏洞描述 廣聯達科技股份有限公司成立于1998年,以建設工程領域專業應用為核心基礎支撐,以產業大數據、產業新金融等為增值服務的數字建筑平臺服務商。廣聯達-Linkworks 協同辦公管理平臺GetUserByUserC…

pytest fixture 用于teardown工作

fixture通過scope參數控制setup級別&#xff0c;setup作為用例之前前的操作&#xff0c;用例執行完之后那肯定也有teardown操作。這里用到fixture的teardown操作并不是獨立的函數&#xff0c;用yield關鍵字呼喚teardown操作。 舉個例子&#xff1a; 輸出&#xff1a; 說明&…

掌握Python的X篇_37_類的實例化、類方法

上篇我們已經學習了python中的類&#xff0c;并且學習到可以通過class關鍵字定義類&#xff0c;而類的最基本特性就是它是一個名稱空間&#xff0c;本篇將會學習類的實例化。 文章目錄 1. 類的實例化1.1__init__函數1.2 實例化流程 2. 類方法與成員 1. 類的實例化 上篇中新定義…

二十二、策略模式

目錄 1、項目需求2、傳統方案解決鴨子問題的分析和代碼實現3、傳統方式實現存在的問題分析和解決方案4、策略模式基本介紹5、使用策略模式解決鴨子問題6、策略模式的注意事項和細節7、策略模式的使用場景 以具體項目來演示為什么需要策略模式&#xff0c;策略模式的優點&#x…

貝銳蒲公英:快速搭建連鎖門店監控體系,賦能企業高效管理

隨著國民生活水平的提高和零售場景的變革&#xff0c;消費者對于餐飲類目的消費支出不斷增加&#xff0c;線下社區生鮮商超作為下沉市場最主要的消費場景之一&#xff0c;蘊藏著巨大價值機會。 對于線下連鎖生鮮超市而言&#xff0c;連鎖門店多、員工多&#xff0c;門店管理時會…

ubuntu磁盤管理

show partition information 掛載設備在這 顯示文件系統信息 build file system mkfs -t ext4 /dev/nvme0n1p4命令作用&#xff1a;將/dev/nvme0n1p4 格式化為 ext4 建立交換分區 mkswap -c -v1 /dev/nvme0n1p4 102400-c&#xff1a;check -v1&#xff1a;新版交換分區 -v0&…

安裝PaddleDetection-2.6.0版本-筆記

安裝PaddleDetection-2.6.0版本-筆記 一、第一步先激活環境 conda activate base conda activate base安裝完paddleDetection后要關閉conda激活環境 conda deactivate conda deactivate二、安裝PaddleDetection2.6.0版本 #pip install PaddleDet2.6.0 #切換版本可安裝pip i…

gitblit windows部署

1.官網下載 往死慢&#xff0c;我是從百度找的1.9.1&#xff0c;幾乎就是最新版 http://www.gitblit.com/ 2.解壓 下載下來是一個zip壓縮包&#xff0c;直接解壓即可 3.配置 3.1.配置資源庫路徑 找到data文件下的gitblit.properties文件&#xff0c;用Notepad打開 **注意路…

詳解編譯過程(編譯+鏈接)

翻譯環境&#xff1a; 編譯&#xff08;編譯器&#xff09;&#xff1a; 1.預編譯&#xff08;預處理&#xff09;&#xff1a; 最終生成test.i文件 【命令】&#xff1a;gcc test.c -E -O test.i 【包含過程】&#xff1a; 1.頭文件的包含 2.注釋的刪除 3.#define定義…

小程序具體開發

window 導航欄 屬性名類型默認值作用navigationBarTitleText string字字符串導航欄標題內容navigationBarBackgroundColorHexcolor#000000設置導航欄背景顏色&#xff08;比如熒黃色 #ffa&#xff09;navigationBarTextStylestringwhite設置導航欄標題的顏色&#xff08;僅含有…

通過將信號頻譜與噪聲頻譜進行比較,自動檢測適當的帶通濾波器轉折頻率研究(Matlab代碼實現)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;歡迎來到本博客????&#x1f4a5;&#x1f4a5; &#x1f3c6;博主優勢&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客內容盡量做到思維縝密&#xff0c;邏輯清晰&#xff0c;為了方便讀者。 ??座右銘&a…

【數據結構與算法】十大經典排序算法-堆排序

&#x1f31f;個人博客&#xff1a;www.hellocode.top &#x1f3f0;Java知識導航&#xff1a;Java-Navigate &#x1f525;CSDN&#xff1a;HelloCode. &#x1f31e;知乎&#xff1a;HelloCode &#x1f334;掘金&#xff1a;HelloCode ?如有問題&#xff0c;歡迎指正&#…

用庫造一個list的輪子 【C++】

文章目錄 list的模擬實現默認成員函數構造函數拷貝構造函數賦值運算符重載析構函數 迭代器迭代器為什么要存在&#xff1f;const_iteratorbegin和end inserterasepush_back && pop_backpush_front &&pop_frontswap 完整代碼 list的模擬實現 默認成員函數 構造…

HCIP BGP小綜合

BGP小綜合 AS配置AS1AS2 中的小自治系統64512AS2 中的小自治系統64513AS3 測試 首先該實驗分成三個AS&#xff0c;AS2里面有聯邦&#xff0c;所以配置順序 要先將IBGP通&#xff0c;然后配置AS1,AS3和聯邦 AS配置 AS1 R1 # bgp 1router-id 1.1.1.1peer 12.1.1.2 as-number …

二十二、責任鏈模式

目錄 1、使用demo演示責任鏈模式2、傳統方案解決oa系統審批3、傳統方案解決oa系統審批存在的問題4、職責鏈模式基本介紹5、職責鏈模式原理類圖6、職責鏈模式解決oa系統采購審批7、職責鏈模式的注意事項和細節8、職責鏈模式的實際使用場景舉例 1、使用demo演示責任鏈模式 學校o…

數據庫相關面試題

鞏固基礎&#xff0c;砥礪前行 。 只有不斷重復&#xff0c;才能做到超越自己。 能堅持把簡單的事情做到極致&#xff0c;也是不容易的。 mysql怎么優化 : MySQL的優化可以從以下幾個方面入手&#xff1a; 數據庫設計優化&#xff1a;合理設計表結構&#xff0c;選擇合適的數…

GitHub 如何部署寫好的H5靜態頁面

感謝粉皮zu的私信&#xff0c;又有素材寫筆記了。(●’?’●) 剛好記錄一下我示例代碼的GitHub部署配置&#xff0c;以便于后期追加倉庫。 效果 環境 gitwin 步驟 第一步 新建倉庫 第二步 拉取代碼 將倉庫clone到本地 git clone 地址第三步 部署文件 新建.github\workflo…

vue-pc端elementui-統一修改問題-Dialog 對話框點擊空白關閉問題-element-所有組件層級問題

前言 實際開發我們經常發現dialog彈出框默認點擊遮罩層空白地方就會關閉-有屬性可以關閉 但是經常會圖方便-或者已經寫完了&#xff0c;不想一個個寫&#xff0c;可以在main.js進行統一關閉 當我們在頁面進行復雜設計和層級關閉改變&#xff0c;會發現右上角的退出登錄彈出款…