1631. Path With Minimum Effort

1631. Path With Minimum Effort

這個題目算是比較變態的一題,但是絕對是面試時間內做得出來的題目,要會這題一定要先做過 875. Koko Eating Bananas 並理解特殊型的 Binary Search

題目的設定條件邏輯並不難,乍看之下,看起來是可以使用深度優先搜索外加,或是透過 Backtrack 來實作一些剪枝。我們要找的是 Minimum Effort ,窮舉出所有的路徑就可以找到最小努力的路徑,實作上的難度也是有點困難,因為我們還需要記錄每個 DFS 到底有沒有超過 Efforts 。

這一題的關鍵是需要「觀察」到這一點,那就是我們能不能找出需要努力的範圍區間呢?答案是可以的,因為我們知道其實最小的努力值就是 0 ,這個情況就是從起點到終點,沒有任何的變化(完全是平地),而最大值需要的努力值就是最高地的值。

接著我們要在這個範圍內,找出最小值,這個最小值剛好需要可以從 (0, 0) 走到 (m - 1, n - 1)

因此在 DFS 的過程中,如果我們找到的努力值比較高,我們可以直接縮小更多的搜尋範圍,如果努力值太小,就會沒有辦法完成 DFS ,我們就可以快速的結束搜尋。

class Solution:
    def minimumEffortPath(self, heights: List[List[int]]) -> int:
        
        rows = len(heights)
        cols = len(heights[0])
        directions = [(1, 0), (0, 1), (-1, 0), (0, -1)]
        
        def dfs(row, col, seen, threshold):
            if row == rows - 1 and col == cols - 1:
                return True
            seen.add((row, col))
            for dr, dc in directions:
                nr, nc = row + dr, col + dc
                if 0 <= nr < rows and 0 <= nc < cols and (nr, nc) not in seen:
                    if abs(heights[nr][nc] - heights[row][col]) <= threshold:
                        if dfs(nr, nc, seen, threshold):
                            return True
            return False
        

        left = 0
        right = max(max(row) for row in heights)
        while left <= right:
            mid = (left + right) // 2
            if dfs(0, 0, set(), mid):
                right = mid - 1
            else:
                left = mid + 1
        
        return left