1631. 最小体力消耗路径
你准备参加一场远足活动。给你一个二维 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] <= 106
原站题解
cpp 解法, 执行用时: 212 ms, 内存消耗: 24 MB, 提交时间: 2023-12-11 00:25:57
class Solution { public: int dir[4][2] = {{0,1},{0,-1},{-1,0},{1,0}}; int minimumEffortPath(vector<vector<int>>& heights) { int m = heights.size(); int n = heights[0].size(); int int_max = 1e6; // 记录到起点到达该点的最小消耗体力 vector<vector<int> > dp(m, vector<int>(n, int_max)); dp[0][0] = 0; queue<int> que; que.push(0); while(!que.empty()) { int cur = que.front(); que.pop(); int r = cur / n; int c = cur % n; for (int i = 0; i < 4; i++) { int nr = r + dir[i][0]; int nc = c + dir[i][1]; if (nr >= 0 && nr < m && nc >= 0 && nc < n) { int temp = max(dp[r][c], abs(heights[nr][nc] - heights[r][c])); if (temp >= dp[nr][nc]) continue; dp[nr][nc] = temp; que.push(nr * n + nc); } } } return dp[m-1][n-1]; } };
javascript 解法, 执行用时: 308 ms, 内存消耗: 69.3 MB, 提交时间: 2023-10-08 23:25:16
/** * @param {number[][]} heights * @return {number} */ var minimumEffortPath = function(heights) { const m = heights.length; const n = heights[0].length; const edges = []; for (let i = 0; i < m; ++i) { for (let j = 0; j < n; ++j) { const id = i * n + j; if (i > 0) { edges.push([id - n, id, Math.abs(heights[i][j] - heights[i - 1][j])]); } if (j > 0) { edges.push([id - 1, id, Math.abs(heights[i][j] - heights[i][j - 1])]); } } } edges.sort((a, b) => a[2] - b[2]); const uf = new UnionFind(m * n); let ans = 0; for (const edge of edges) { const x = edge[0], y = edge[1], v = edge[2]; uf.unite(x, y); if (uf.connected(0, m * n - 1)) { ans = v; break; } } return ans; }; // 并查集模板 class UnionFind { constructor (n) { this.parent = new Array(n).fill(0).map((element, index) => index); this.size = new Array(n).fill(1); // 当前连通分量数目 this.setCount = n; } findset (x) { if (this.parent[x] === x) { return x; } this.parent[x] = this.findset(this.parent[x]); return this.parent[x]; } unite (a, b) { let x = this.findset(a), y = this.findset(b); if (x === y) { return false; } if (this.size[x] < this.size[y]) { [x, y] = [y, x]; } this.parent[y] = x; this.size[x] += this.size[y]; this.setCount -= 1; return true; } connected (a, b) { const x = this.findset(a), y = this.findset(b); return x === y; } }
javascript 解法, 执行用时: 268 ms, 内存消耗: 49.3 MB, 提交时间: 2023-10-08 23:24:56
/** * @param {number[][]} heights * @return {number} */ var minimumEffortPath = function(heights) { const dirs = [[-1, 0], [1, 0], [0, -1], [0, 1]]; const m = heights.length, n = heights[0].length; let left = 0, right = 999999, ans = 0; while (left <= right) { const mid = Math.floor((left + right) / 2); const queue = [[0, 0]]; const seen = new Array(m * n).fill(0); seen[0] = 1; while (queue.length) { const [x, y] = queue.shift(); for (let i = 0; i < 4; i++) { const nx = x + dirs[i][0]; const ny = y + dirs[i][1]; if (nx >= 0 && nx < m && ny >= 0 && ny < n && !seen[nx * n + ny] && Math.abs(heights[x][y] - heights[nx][ny]) <= mid) { queue.push([nx, ny]); seen[nx * n + ny] = 1; } } } if (seen[m * n - 1]) { ans = mid; right = mid - 1; } else { left = mid + 1; } } return ans; };
golang 解法, 执行用时: 124 ms, 内存消耗: 7.5 MB, 提交时间: 2023-10-08 23:24:42
type pair struct{ x, y int } var dirs = []pair{{-1, 0}, {1, 0}, {0, -1}, {0, 1}} func minimumEffortPath(heights [][]int) int { n, m := len(heights), len(heights[0]) return sort.Search(1e6, func(maxHeightDiff int) bool { vis := make([][]bool, n) for i := range vis { vis[i] = make([]bool, m) } vis[0][0] = true queue := []pair{{}} for len(queue) > 0 { p := queue[0] queue = queue[1:] if p.x == n-1 && p.y == m-1 { return true } for _, d := range dirs { x, y := p.x+d.x, p.y+d.y if 0 <= x && x < n && 0 <= y && y < m && !vis[x][y] && abs(heights[x][y]-heights[p.x][p.y]) <= maxHeightDiff { vis[x][y] = true queue = append(queue, pair{x, y}) } } } return false }) } func abs(x int) int { if x < 0 { return -x } return x }
golang 解法, 执行用时: 100 ms, 内存消耗: 7.6 MB, 提交时间: 2023-10-08 23:24:31
type unionFind struct { parent, size []int } func newUnionFind(n int) *unionFind { parent := make([]int, n) size := make([]int, n) for i := range parent { parent[i] = i size[i] = 1 } return &unionFind{parent, size} } func (uf *unionFind) find(x int) int { if uf.parent[x] != x { uf.parent[x] = uf.find(uf.parent[x]) } return uf.parent[x] } func (uf *unionFind) union(x, y int) { fx, fy := uf.find(x), uf.find(y) if fx == fy { return } if uf.size[fx] < uf.size[fy] { fx, fy = fy, fx } uf.size[fx] += uf.size[fy] uf.parent[fy] = fx } func (uf *unionFind) inSameSet(x, y int) bool { return uf.find(x) == uf.find(y) } type edge struct { v, w, diff int } func minimumEffortPath(heights [][]int) int { n, m := len(heights), len(heights[0]) edges := []edge{} for i, row := range heights { for j, h := range row { id := i*m + j if i > 0 { edges = append(edges, edge{id - m, id, abs(h - heights[i-1][j])}) } if j > 0 { edges = append(edges, edge{id - 1, id, abs(h - heights[i][j-1])}) } } } sort.Slice(edges, func(i, j int) bool { return edges[i].diff < edges[j].diff }) uf := newUnionFind(n * m) for _, e := range edges { uf.union(e.v, e.w) if uf.inSameSet(0, n*m-1) { return e.diff } } return 0 } func abs(x int) int { if x < 0 { return -x } return x }
golang 解法, 执行用时: 64 ms, 内存消耗: 7.1 MB, 提交时间: 2023-10-08 23:24:16
type point struct{ x, y, maxDiff int } type hp []point func (h hp) Len() int { return len(h) } func (h hp) Less(i, j int) bool { return h[i].maxDiff < h[j].maxDiff } func (h hp) Swap(i, j int) { h[i], h[j] = h[j], h[i] } func (h *hp) Push(v interface{}) { *h = append(*h, v.(point)) } func (h *hp) Pop() (v interface{}) { a := *h; *h, v = a[:len(a)-1], a[len(a)-1]; return } type pair struct{ x, y int } var dir4 = []pair{{-1, 0}, {1, 0}, {0, -1}, {0, 1}} func minimumEffortPath(heights [][]int) int { n, m := len(heights), len(heights[0]) maxDiff := make([][]int, n) for i := range maxDiff { maxDiff[i] = make([]int, m) for j := range maxDiff[i] { maxDiff[i][j] = math.MaxInt64 } } maxDiff[0][0] = 0 h := &hp{{}} for { p := heap.Pop(h).(point) if p.x == n-1 && p.y == m-1 { return p.maxDiff } if maxDiff[p.x][p.y] < p.maxDiff { continue } for _, d := range dir4 { if x, y := p.x+d.x, p.y+d.y; 0 <= x && x < n && 0 <= y && y < m { if diff := max(p.maxDiff, abs(heights[x][y]-heights[p.x][p.y])); diff < maxDiff[x][y] { maxDiff[x][y] = diff heap.Push(h, point{x, y, diff}) } } } } } func max(a, b int) int { if a > b { return a } return b } func abs(x int) int { if x < 0 { return -x } return x }
python3 解法, 执行用时: 3240 ms, 内存消耗: 18 MB, 提交时间: 2023-10-08 23:22:50
# 并查集模板 class UnionFind: def __init__(self, n: int): self.parent = list(range(n)) self.size = [1] * n self.n = n # 当前连通分量数目 self.setCount = n def findset(self, x: int) -> int: if self.parent[x] == x: return x self.parent[x] = self.findset(self.parent[x]) return self.parent[x] def unite(self, x: int, y: int) -> bool: x, y = self.findset(x), self.findset(y) if x == y: return False if self.size[x] < self.size[y]: x, y = y, x self.parent[y] = x self.size[x] += self.size[y] self.setCount -= 1 return True def connected(self, x: int, y: int) -> bool: x, y = self.findset(x), self.findset(y) return x == y class Solution: # 二分 def minimumEffortPath(self, heights: List[List[int]]) -> int: m, n = len(heights), len(heights[0]) left, right, ans = 0, 10**6 - 1, 0 while left <= right: mid = (left + right) // 2 q = collections.deque([(0, 0)]) seen = {(0, 0)} while q: x, y = q.popleft() for nx, ny in [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)]: if 0 <= nx < m and 0 <= ny < n and (nx, ny) not in seen and abs(heights[x][y] - heights[nx][ny]) <= mid: q.append((nx, ny)) seen.add((nx, ny)) if (m - 1, n - 1) in seen: ans = mid right = mid - 1 else: left = mid + 1 return ans # 并查集 def minimumEffortPath2(self, heights: List[List[int]]) -> int: m, n = len(heights), len(heights[0]) edges = list() for i in range(m): for j in range(n): iden = i * n + j if i > 0: edges.append((iden - n, iden, abs(heights[i][j] - heights[i - 1][j]))) if j > 0: edges.append((iden - 1, iden, abs(heights[i][j] - heights[i][j - 1]))) edges.sort(key=lambda e: e[2]) uf = UnionFind(m * n) ans = 0 for x, y, v in edges: uf.unite(x, y) if uf.connected(0, m * n - 1): ans = v break return ans # Dijkstra 最短路 def minimumEffortPath3(self, heights: List[List[int]]) -> int: m, n = len(heights), len(heights[0]) q = [(0, 0, 0)] dist = [0] + [float("inf")] * (m * n - 1) seen = set() while q: d, x, y = heapq.heappop(q) iden = x * n + y if iden in seen: continue if (x, y) == (m - 1, n - 1): break seen.add(iden) for nx, ny in [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)]: if 0 <= nx < m and 0 <= ny < n and max(d, abs(heights[x][y] - heights[nx][ny])) <= dist[nx * n + ny]: dist[nx * n + ny] = max(d, abs(heights[x][y] - heights[nx][ny])) heapq.heappush(q, (dist[nx * n + ny], nx, ny)) return dist[m * n - 1]
java 解法, 执行用时: 165 ms, 内存消耗: 42.7 MB, 提交时间: 2023-10-08 23:20:49
class Solution { int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; public int minimumEffortPath(int[][] heights) { int m = heights.length; int n = heights[0].length; int left = 0, right = 999999, ans = 0; while (left <= right) { int mid = (left + right) / 2; Queue<int[]> queue = new LinkedList<int[]>(); queue.offer(new int[]{0, 0}); boolean[] seen = new boolean[m * n]; seen[0] = true; while (!queue.isEmpty()) { int[] cell = queue.poll(); int x = cell[0], y = cell[1]; for (int i = 0; i < 4; ++i) { int nx = x + dirs[i][0]; int ny = y + dirs[i][1]; if (nx >= 0 && nx < m && ny >= 0 && ny < n && !seen[nx * n + ny] && Math.abs(heights[x][y] - heights[nx][ny]) <= mid) { queue.offer(new int[]{nx, ny}); seen[nx * n + ny] = true; } } } if (seen[m * n - 1]) { ans = mid; right = mid - 1; } else { left = mid + 1; } } return ans; } }
java 解法, 执行用时: 89 ms, 内存消耗: 43.4 MB, 提交时间: 2023-10-08 23:20:38
class Solution { public int minimumEffortPath(int[][] heights) { int m = heights.length; int n = heights[0].length; List<int[]> edges = new ArrayList<int[]>(); for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { int id = i * n + j; if (i > 0) { edges.add(new int[]{id - n, id, Math.abs(heights[i][j] - heights[i - 1][j])}); } if (j > 0) { edges.add(new int[]{id - 1, id, Math.abs(heights[i][j] - heights[i][j - 1])}); } } } Collections.sort(edges, new Comparator<int[]>() { public int compare(int[] edge1, int[] edge2) { return edge1[2] - edge2[2]; } }); UnionFind uf = new UnionFind(m * n); int ans = 0; for (int[] edge : edges) { int x = edge[0], y = edge[1], v = edge[2]; uf.unite(x, y); if (uf.connected(0, m * n - 1)) { ans = v; break; } } return ans; } } // 并查集模板 class UnionFind { int[] parent; int[] size; int n; // 当前连通分量数目 int setCount; public UnionFind(int n) { this.n = n; this.setCount = n; this.parent = new int[n]; this.size = new int[n]; Arrays.fill(size, 1); for (int i = 0; i < n; ++i) { parent[i] = i; } } public int findset(int x) { return parent[x] == x ? x : (parent[x] = findset(parent[x])); } public boolean unite(int x, int y) { x = findset(x); y = findset(y); if (x == y) { return false; } if (size[x] < size[y]) { int temp = x; x = y; y = temp; } parent[y] = x; size[x] += size[y]; --setCount; return true; } public boolean connected(int x, int y) { x = findset(x); y = findset(y); return x == y; } }
java 解法, 执行用时: 48 ms, 内存消耗: 42.8 MB, 提交时间: 2023-10-08 23:20:17
class Solution { int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; public int minimumEffortPath(int[][] heights) { int m = heights.length; int n = heights[0].length; PriorityQueue<int[]> pq = new PriorityQueue<int[]>(new Comparator<int[]>() { public int compare(int[] edge1, int[] edge2) { return edge1[2] - edge2[2]; } }); pq.offer(new int[]{0, 0, 0}); int[] dist = new int[m * n]; Arrays.fill(dist, Integer.MAX_VALUE); dist[0] = 0; boolean[] seen = new boolean[m * n]; while (!pq.isEmpty()) { int[] edge = pq.poll(); int x = edge[0], y = edge[1], d = edge[2]; int id = x * n + y; if (seen[id]) { continue; } if (x == m - 1 && y == n - 1) { break; } seen[id] = true; for (int i = 0; i < 4; ++i) { int nx = x + dirs[i][0]; int ny = y + dirs[i][1]; if (nx >= 0 && nx < m && ny >= 0 && ny < n && Math.max(d, Math.abs(heights[x][y] - heights[nx][ny])) < dist[nx * n + ny]) { dist[nx * n + ny] = Math.max(d, Math.abs(heights[x][y] - heights[nx][ny])); pq.offer(new int[]{nx, ny, dist[nx * n + ny]}); } } } return dist[m * n - 1]; } }
cpp 解法, 执行用时: 120 ms, 内存消耗: 20.3 MB, 提交时间: 2023-10-08 23:20:04
class Solution { private: static constexpr int dirs[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; public: int minimumEffortPath(vector<vector<int>>& heights) { int m = heights.size(); int n = heights[0].size(); auto tupleCmp = [](const auto& e1, const auto& e2) { auto&& [x1, y1, d1] = e1; auto&& [x2, y2, d2] = e2; return d1 > d2; }; priority_queue<tuple<int, int, int>, vector<tuple<int, int, int>>, decltype(tupleCmp)> q(tupleCmp); q.emplace(0, 0, 0); vector<int> dist(m * n, INT_MAX); dist[0] = 0; vector<int> seen(m * n); while (!q.empty()) { auto [x, y, d] = q.top(); q.pop(); int id = x * n + y; if (seen[id]) { continue; } if (x == m - 1 && y == n - 1) { break; } seen[id] = 1; for (int i = 0; i < 4; ++i) { int nx = x + dirs[i][0]; int ny = y + dirs[i][1]; if (nx >= 0 && nx < m && ny >= 0 && ny < n && max(d, abs(heights[x][y] - heights[nx][ny])) < dist[nx * n + ny]) { dist[nx * n + ny] = max(d, abs(heights[x][y] - heights[nx][ny])); q.emplace(nx, ny, dist[nx * n + ny]); } } } return dist[m * n - 1]; } };
cpp 解法, 执行用时: 148 ms, 内存消耗: 29.4 MB, 提交时间: 2023-10-08 23:19:51
// 并查集模板 class UnionFind { public: vector<int> parent; vector<int> size; int n; // 当前连通分量数目 int setCount; public: UnionFind(int _n): n(_n), setCount(_n), parent(_n), size(_n, 1) { iota(parent.begin(), parent.end(), 0); } int findset(int x) { return parent[x] == x ? x : parent[x] = findset(parent[x]); } bool unite(int x, int y) { x = findset(x); y = findset(y); if (x == y) { return false; } if (size[x] < size[y]) { swap(x, y); } parent[y] = x; size[x] += size[y]; --setCount; return true; } bool connected(int x, int y) { x = findset(x); y = findset(y); return x == y; } }; class Solution { public: int minimumEffortPath(vector<vector<int>>& heights) { int m = heights.size(); int n = heights[0].size(); vector<tuple<int, int, int>> edges; for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { int id = i * n + j; if (i > 0) { edges.emplace_back(id - n, id, abs(heights[i][j] - heights[i - 1][j])); } if (j > 0) { edges.emplace_back(id - 1, id, abs(heights[i][j] - heights[i][j - 1])); } } } sort(edges.begin(), edges.end(), [](const auto& e1, const auto& e2) { auto&& [x1, y1, v1] = e1; auto&& [x2, y2, v2] = e2; return v1 < v2; }); UnionFind uf(m * n); int ans = 0; for (const auto [x, y, v]: edges) { uf.unite(x, y); if (uf.connected(0, m * n - 1)) { ans = v; break; } } return ans; } };
cpp 解法, 执行用时: 336 ms, 内存消耗: 61.5 MB, 提交时间: 2023-10-08 23:19:34
class Solution { private: static constexpr int dirs[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; public: // 二分 int minimumEffortPath(vector<vector<int>>& heights) { int m = heights.size(); int n = heights[0].size(); int left = 0, right = 999999, ans = 0; while (left <= right) { int mid = (left + right) / 2; queue<pair<int, int>> q; q.emplace(0, 0); vector<int> seen(m * n); seen[0] = 1; while (!q.empty()) { auto [x, y] = q.front(); q.pop(); for (int i = 0; i < 4; ++i) { int nx = x + dirs[i][0]; int ny = y + dirs[i][1]; if (nx >= 0 && nx < m && ny >= 0 && ny < n && !seen[nx * n + ny] && abs(heights[x][y] - heights[nx][ny]) <= mid) { q.emplace(nx, ny); seen[nx * n + ny] = 1; } } } if (seen[m * n - 1]) { ans = mid; right = mid - 1; } else { left = mid + 1; } } return ans; } };