遞迴與動態規劃 換錢的方法數

2021-08-06 03:11:16 字數 4543 閱讀 3466

【問題】

給定陣列arr,arr中所有的值都為整數且不重複。每個值代表一種面值的貨幣,每種貨幣有無數張,再給定乙個整數aim代表要找的錢數,求換錢的方法有多少種。

【基本思路】

這道題的經典之處在於它可以體現暴力遞迴、記憶搜尋、動態規劃之間的關係,並可以在動態規劃的基礎上再進行一次優化。

首先介紹暴力遞迴的方法。如果arr = [5, 10, 25, 1],aim = 1000,分析過程如下:

用0張5元的貨幣,讓[10, 25, 1]組成剩下的1000,最終方法數記為res1。

用1張5元的貨幣,讓[10, 25, 1]組成剩下的995,最終方法數記為res2。

用2張5元的貨幣,讓[10, 25, 1]組成剩下的990,最終方法數記為res3。

……用201張5元的貨幣,讓[10, 25, 1]組成剩下的0,最終方法數記為res201。

那麼res1 + res2 + res3 + …… +res201的值就是中的方法數。根據如上的分析過程定義遞迴函式process1(arr, index, aim)它的含義是如果用arr[index..n-1]這些面值的錢組成aim,返回總的方法數。最壞情況下時間複雜度為o(aim^n),n表示陣列的長度。

下面是用python3實現的**

#暴力遞迴方法

defcoins1

(arr, aim):

defprocess1

(arr, index, aim):

if index == len(arr):

return

1if aim == 0

else

0else:

res = 0

for i in range(0, aim//arr[index]+1):

res += process1(arr, index+1, aim-arr[index]*i)

return res

if arr == none

or len(arr) == 0

or aim < 0:

return

0return process1(arr, 0, aim)

記憶搜尋的方法。在暴力遞迴中,有很多的重複計算,比如使用0張5元+1張10元的情況和使用2張5元+0張10元的情況,都需要求[25, 1]組成剩下的990的方法數。記憶搜尋就是使用一張記錄表將遞迴過程中的結果進行記錄,當下次再遇到同樣的遞迴過程,就直接使用表中的資料,這樣就對暴力遞迴進行了優化。

時間複雜度為o(n*aim^2),空間複雜度o(n*aim)。

#記憶搜尋方法

defcoins2

(arr, aim):

defprocess2

(arr, index, aim, records):

if index == len(arr):

return

1if aim == 0

else

0else:

res = 0

for i in range(0, aim//arr[index]+1):

mapvalue = records[index+1][aim-arr[index]*i]

if mapvalue != 0:

res += mapvalue if mapvalue != -1

else

0else:

res += process2(arr, index+1, aim-arr[index]*i, records)

records[index][aim] = -1

if res == 0

else res

return res

if arr == none

or len(arr) == 0

or aim < 0:

return

0 records = [[0

for i in range(aim+1)] for j in range(len(arr)+1)]

return process2(arr, 0, aim, records)

動態規劃的方法。首先生成行數為n、列數為aim+1的dp矩陣,dp[i][j]的含義是在使用arr[0…i]貨幣的前提下,組成錢數j有多少種方法。dp[i][j]的值求法如下:

對於矩陣的第一行,表示只使用貨幣arr[0]的情況下,組成錢的方法數,可以組成的錢數是arr[0]的整倍數,所以將其設定成1

對於矩陣的第一列,表示組成錢數0的方法數,很明顯是1種,也就是不使用任何貨幣,所以第一列都設定為1

對於矩陣的其他位置,dp[i][j]的值來自以下幾個值的累加:完全不使用貨幣arr[i],使用一張貨幣arr[i],使用兩張貨幣arr[i]…使用k張貨幣arr[i](k=aim//arr[i]),對應到dp表裡分別是[i-1][j]、dp[i-1][j-arr[i]]、dp[i-1][j-2*arr[i]]…dp[i-1][j-k*arr[i]]。

時間複雜度為o(n*aim^2),空間複雜度o(n*aim)。

#動態規劃方法

defcoins3

(arr, aim):

if arr == none

or len(arr) == 0

or aim < 0:

return

0 row = len(arr)

dp = [[0

for i in range(aim+1)]for j in range(row)]

for i in range(row):

dp[i][0] = 1

for j in range(1, aim//arr[0]+1):

dp[0][arr[0]*j] = 1

for i in range(1, row):

for j in range(1, aim+1):

num = 0

for k in range(j//arr[i]+1):

num += dp[i-1][j-arr[i]*k]

dp[i][j] = num

return dp[row-1][aim]

對動態規劃進行優化。上述動態規劃中計算dp[i][j]的過程比較繁瑣,其實計算dp的第三步可以進行優化。

在上述動態規劃的第三步中,dp[i][j] = dp[i-1][j] + dp[i-1][j-arr[i]] + dp[i-1][j-2*arr[i]] + …dp[i-1][j-k*arr[i]]。我們發現,這個等式中除了第一項以外其他項的累加和其實就是dp[i][j-arr[i]],只不過是將j-arr[i]這個整體當作j,所以步驟3可以優化為dp[i][j] = dp[i-1][j] + dp[i][j-arr[i]]。

時間複雜度為o(n*aim),空間複雜度o(n*aim)。

#動態規劃公升級版

defcoins4

(arr, aim):

if arr == none

or len(arr) == 0

or aim < 0:

return

0 row = len(arr)

dp = [[0

for i in range(aim+1)] for j in range(row)]

for i in range(row):

dp[i][0] = 1

for j in range(1, aim//arr[0]+1):

dp[0][arr[0]*j] = 1

for i in range(1,row):

for j in range(1, aim+1):

dp[i][j] = dp[i-1][j]

dp[i][j] += dp[i][j-arr[i]] if j-arr[i] >= 0

else

0return dp[row-1][aim]

對動態規劃進行再優化。即使用空間壓縮的方法,優化動態規劃的空間複雜度,只是用一維陣列記錄資料而不是矩陣。

時間複雜度為o(n*aim),空間複雜度o(aim)。

#動態規劃公升級版+空間壓縮

defcoins5

(arr, aim):

if arr == none

or len(arr) == 0

or aim < 0:

return

0 dp = [0

for i in range(aim+1)]

for i in range(aim//arr[0]+1):

dp[arr[0]*i] = 1

for i in range(len(arr)):

for j in range(1, aim+1):

dp[j] += dp[j-arr[i]] if j-arr[i] >= 0

else

0return dp[aim]

動態規劃 換錢的方法數

演算法專題導航頁面 題目描述 給定陣列arr,設陣列長度為n,arr中所有的值都為正整數且不重複。每個值代表一種面值的貨幣,每種面值的貨幣可以使用任意張,再給定乙個整數aim,代表要找的錢數,求換錢的方法數有多少種。由於方法的種數比較大,所以要求輸出對10 9 7進行取模後的答案。輸入描述 輸入包括...

換錢的方法數(動態規劃講的很好)

給定陣列arr,arr中所有的值都為正數且不重複。每個值代表一種面值的貨幣,每種面值的貨幣可以使用任意張,再給定乙個整數aim代表要找的錢數,求換錢有多少種方法。舉例 arr 5,10,25,1 aim 0。組成0元的方法有1種,就是所有面值的貨幣都不用。所以返回1。arr 5,10,25,1 ai...

動態規劃 換錢方法

換錢方法 給定陣列arr,arr中所有的值都是整數且不重複,每個值代表一種面值的貨幣,每種面值的貨幣可以使用任意張,再給定乙個整數,aim代表要找的錢數,求換錢有多少種方法。思路 1.建立一張二維表dp,m n 其中m表示貨幣的種類,行n表示 target 1 即是目標錢數 1 即是 下表以目標錢數...