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