322 零錢兌換

2021-10-22 07:45:24 字數 3418 閱讀 3976

# 最值問題

# 最優子結構特性

# 子問題重疊特性

# 使用動態規劃:狀態轉移方程

# 列出狀態轉移方程

# 步驟:

# 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 零錢兌換 官方題解 搜尋回溯 超出時間限制 動態規劃 自頂向下 動態規劃 自...