# 最值問題
# 最優子結構特性
# 子問題重疊特性
# 使用動態規劃:狀態轉移方程
# 列出狀態轉移方程
# 步驟:
# 1. 明確狀態:原問題和子問題中變化的量:amount(連續性變化)
# 2. 定義動態規劃陣列/函式的含義:dp[n]的含義:湊成n需要的硬幣個數
# 3. 明確選擇:對於每個狀態,可以做出什麼選擇來改變當前狀態
# 選乙個硬幣(+1),amout就會減少
# 4. 明確base case:無解的情況,或者輸出為0的情況
# 當amount<0時,為-1
# 當amount=0時,為0
""" 框架:1.產生出最優子結構
def coinchange:
dp(n):
if n == 0:
return 0
if n < 0:
return -1
res = math.inf # 求最小值用無窮大打底
for coin in coins:
subproblem = dp(n - coins) # 將原問題分解成子問題
# 子問題沒有解,跳過,嘗試下乙個子問題
if subproblem == -1:
continue # 再嘗試別的硬幣
# 結果
res = min(res, subproblem + 1)
return res if res != math.inf else -1
return df(amount)
2.解決子問題重疊造成的空間浪費(使用備忘錄)(實際上也可以作為最終的答案)
def coinchange:
memo = {} # n:meno[n]
dp(n):
if n in memo:
return memo[n] # 1.從備忘錄返回
if n == 0:
return 0
if n < 0:
return -1
res = math.inf
for coin in coins: 迴圈時間複雜度為常數級o(k)
subproblem = dp(n - coins) # 將原問題分解成子問題
# 子問題沒有解,跳過,嘗試下乙個子問題
if subproblem == -1:
continue # 再嘗試別的硬幣
res = min(res, subproblem + 1)
# 結果
memo[n] = res if res != math.inf else -1
return memo[n]
return df(amount)
遞迴的時間複雜度:總的遞迴輪數*單個遞迴的時間複雜度
遞迴往往可以通過迭代改寫
從時間複雜度上是優良的o(kn)
"""dp =
0)# 先解決特殊問題
# 使用索引作為變化的amount
for i in
range(1
, amount+1)
:# 注意範圍1)
# 和無窮大的作用是一樣的
for i in
range(0
, amount+1)
:# amount從0開始變化
# 將原問題分解為子問題
for coin in coins:
if i - coin <0:
# 子問題無解,返回
continue
dp[i]
=min
(dp[i]
, dp[i-coin]+1
)# 儲存結果
return-1
if dp[amount]
== amount+
1else dp[amount]
# 只需要返回最終的結果
class
solution
:def
coinchange
(self, coins: list[
int]
, amount:
int)
->
int:
n =len(coins)
coins.sort(reverse=
true
)# 先給硬幣排序,降序
self.res =
float
("inf"
)# 定義最小的使用硬幣的數量為self.res
defdfs
(index,target,count)
:# 定義深度優先搜尋演算法
coin = coins[index]
if math.ceil(target/coin)
+count>=self.res:
# 當不是最優解的時候
# 剪枝函式:target/coin不能整除
# 可能的情況1:湊不成
# 可能的情況2:能湊成
# 統一的處理辦法:+1,不是當前最優值就剪枝,不再進行嘗試
return
if target%coin==0:
# 當能被整除時
self.res = count+target//coin # 更新當前最優解
return
if index==n-1:
# 當前嘗試最小的面值且最小的面值不能被整除時
return
for j in
range
(target//coin,-1
,-1)
:# 能湊成且可能出現最優值的時候
# 當前coin可能用target//coin張,也可能不用,在j這個區間內
# 將j對應的所有可能的情況進行嘗試
# 進行下一層
dfs(index+
1,target-j*coin,count+j)
dfs(
0,amount,0)
# 當前嘗試的面額,當前的連續變數,當前的最優解
return
int(self.res)
if self.res!=
float
("inf"
)else-1
# 無窮大的時候就是-1
322 零錢兌換
class solution 不是大的取的越多越好,大的取的很多,最後不能剛好取到,比如22,陣列是10,6,你直接取兩個10肯定不行第乙個數有取1個,取0個或者取多個好幾種取法。比如18,你有10和6,那麼10乙個都不能取 所以要考慮的只是當前這一位可以取幾個 for int i left coi...
322 零錢兌換
給定不同面額的硬幣 coins 和乙個總金額 amount。編寫乙個函式來計算可以湊成總金額所需的最少的硬幣個數。如果沒有任何一種硬幣組合能組成總金額,返回 1。示例 1 輸入 coins 1,2,5 amount 11 輸出 3 解釋 11 5 5 1 示例 2 輸入 coins 2 amount...
322 零錢兌換
leetcode 322 零錢兌換 給定不同面額的硬幣 coins 和乙個總金額 amount。編寫乙個函式來計算可以湊成總金額所需的最少的硬幣個數。如果沒有任何一種硬幣組合能組成總金額,返回 1。leetcode 322 零錢兌換 官方題解 搜尋回溯 超出時間限制 動態規劃 自頂向下 動態規劃 自...