1. 程式人生 > >DP專題9 - leetcode329. Longest Increasing Path in a Matrix/322. Coin Change -經典

DP專題9 - leetcode329. Longest Increasing Path in a Matrix/322. Coin Change -經典

329. Longest Increasing Path in a Matrix - 記憶化搜尋DP

題目描述

給定一個正整數矩陣,找出最長遞增路徑的長度。
第每個格子,你可以向四個方向移動(上下左右),不能對角線或移出邊界。

例子
Example 1:

Input: nums =
[
[9,9,4],
[6,6,8],
[2,1,1]
]
Output: 4

Explanation: The longest increasing path is [1, 2, 6, 9].

Example 2:

Input: nums =
[
[3,4,5],
[3,2,6],
[2,2,1]
]
Output: 4

Explanation: The longest increasing path is [3, 4, 5, 6]. Moving diagonally is not allowed.

思想
dp[i][j]表示到達(i, j)時的最大長度。
狀態方程:有四種狀態(a, b)可以到達(i, j),若當前matrix[i][j]比前一個格子大,則更新matrix[i][j] = max(matrix[i][j], dp(a, b)] + 1)
採取記憶化搜尋遞迴的方法,如果當前點更新過了dp[i][j] != 1,則無需狀態轉移。

解法
遞迴。複雜度 - 時間O(n^2), 空間O(n^2)。

class Solution(object):
    def longestIncreasingPath(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: int
        """
        if not matrix or not matrix[0]:
            return 0
        
        m, n = len(matrix), len(matrix[0])
        dp = [[1] * n for _ in range
(m)] # dp[i][j]以matrix[i][j]結尾的LIS dxy = [(-1, 0), (0, -1), (1, 0), (0, 1)] def dfs(x, y): if dp[x][y] != 1: return dp[x][y] for dx, dy in dxy: nx, ny = x + dx, y + dy if 0 <= nx < m and 0 <= ny < n and matrix[x][y] > matrix[nx][ny]: dp[x][y] = max(dp[x][y], dfs(nx, ny) + 1) return dp[x][y] res = 1 for i in range(m): for j in range(n): res = max(res, dfs(i, j)) return res

(簡化版)

class Solution(object):
    def longestIncreasingPath(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: int
        """
        if not matrix or not matrix[0]:
            return 0
        m, n = len(matrix), len(matrix[0])
        dp = [[1] * n for _ in range(m)]
        
        def dfs(i, j):
            if dp[i][j] == 1:
                dp[i][j] = 1 + max(dfs(i-1, j) if i > 0 and matrix[i][j] > matrix[i-1][j] else 0,
                                  dfs(i+1, j) if i < m-1 and matrix[i][j] > matrix[i+1][j] else 0,
                                  dfs(i, j-1) if j > 0 and matrix[i][j] > matrix[i][j-1] else 0,
                                  dfs(i, j+1) if j < n-1 and matrix[i][j] > matrix[i][j+1] else 0)
            return dp[i][j]
        
        return max(dfs(i, j) for i in range(m) for j in range(n))

322. Coin Change

題目描述

給定不同面值的硬幣和總錢數amount。求得到總錢數所需的最小硬幣數。如果無法得到總錢數,返回-1。
假設每種硬幣有無限個。

例子
Example 1:

Input: coins = [1, 2, 5], amount = 11
Output: 3

Explanation: 11 = 5 + 5 + 1

Example 2:

Input: coins = [2], amount = 3
Output: -1

思想
(DP - 揹包問題)用amount+1標記無窮。
初始化:前0種組成價值0所需數目為0, 前0種組成價值j(j>0)所需數目為無窮。
轉移方程
當j >= coins[i-1]時,至少取一個coins[i-1]:dp[i][j - coins[i-1]] + 1;或一次也不取coins[i-1]:dp[i-1][j]。即dp[i][j] = min(dp[i][j - coins[i-1]] + 1, dp[i-1][j]);
當j < coins[i-1]時,只能一次也不取coins[i-1]:dp[i-1][j]。即dp[i][j] = dp[i-1][j]。
(優化)
索引coins時的優化和空間優化

解法1
DP。複雜度:時間 - O(mn),空間-O(mn)

class Solution(object):
    def coinChange(self, coins, amount):
        """
        :type coins: List[int]
        :type amount: int
        :rtype: int
        """
        n = len(coins)
        dp = [[amount+1] * (amount+1) for _ in range(n+1)]    # dp[i][j]表示用前i種coins組成價值j所需的最小數量
        dp[0][0] = 0
        
        for i in range(1, n+1):
            for j in range(amount+1):
                if j >= coins[i-1]:
                    dp[i][j] = min(dp[i][j - coins[i-1]] + 1, dp[i-1][j])# 至少用一次coin[i-1]/一次也不用
                else:
                    dp[i][j] = dp[i-1][j]
        return -1 if dp[-1][-1] == amount+1 else dp[-1][-1]

解法2 - 優化
DP。複雜度:時間 - O(mn),空間-O(mn)

class Solution(object):
    def coinChange(self, coins, amount):
        """
        :type coins: List[int]
        :type amount: int
        :rtype: int
        """
        n = len(coins)
        dp = [amount+1] * (amount+1)
        dp[0] = 0
        
        for coin in coins:
            for j in range(amount+1):
                if j >= coin:
                    dp[j] = min(dp[j-coin] + 1, dp[j])
        return dp[-1] if dp[-1] != amount + 1 else -1