換錢的方法數

2021-07-07 09:11:26 字數 2669 閱讀 7261

題目:

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

舉例:

arr = [5, 10, 25, 1], aim = 0

組成0元的方法有1種,就是所有面值的貨幣都不用。所以返回1。

arr = [5, 10, 25, 1] , aim = 15

組成15元的方法有6種,分別為3張5元、1張10元+1張5元、1張10元+5張1元、10張1元+1張5元、2張5元+5元一張和15元一張。所以返回6。

arr = [3, 5] , aim = 2

任何方法都無法組成2元。所以返回0。

【詳解】

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

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

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

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

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

………

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

那麼res1 + res2 + … + res201的值就是總的方法數。根據如上的分析過程定義遞迴函式process(arr, index, aim),它的含義是如何用arr[index…n-1]這些面值的錢組成aim,返回總的方法數。

#include

#include

using

namespace

std;

int process(vector

arr, int index, int aim)

else

return res;

}int coins(vector

arr, int aim)

int main()

; cout

<<"coins(arr, 1) = "

<0])<<"coins(arr, 15) = "

<1])<<"coins(arr, 1000) = "

<2])0;}

暴力遞迴之所以暴力,是因為存在著大量的重複計算。還是用上面說過的例子。當已經使用0張5元+1張10元的情況下,後續應該求[25, 1]組成剩下的990的方法總數。當已經使用2張5元+0張10元的情況下,後續還是求[25, 1]組成剩下的990的方法總數。兩種情況下都需要求process(arr, 2, 990)。類似這樣的重複計算在暴力遞迴的過程中大量產生,所以暴力遞迴方法的時間複雜度非常高,並且與arr中錢的面值有關,最差的情況下為o(aim^n)。

下面我們進行一些優化處理,process(arr, index, aim)中arr始終不變的,變化的只有index和aim,所以可以用p(index, aim)表示乙個遞迴的過程。重複計算之所以大量纏身,是因為沒乙個遞迴過程都沒記下來,所以下次還要重複去求,所以可以事先準備好乙個map,每計算完乙個遞迴過程,都將結果記錄到map中。當下次進行同樣的遞迴過程之前,現在map中查詢這個遞迴過程是否已經計算過,如果已經計算過,就把值拿出來直接用,如果沒有計算過,需要再進入遞迴過程。具體看下面的**中的coins2方法,它和coins1方法的區別就是準備好全域性變數map,記錄已經計算過的遞迴過程的結果,防止下次重複計算。因為本題的遞迴過程可由兩個變數表示,所以map是一張二維表。map[i][j]表示遞迴過程p(i,j)的返回值。另外還有一些特別值,map[i][j]表示遞迴過程p(i,j)從來沒有計算過。map[i][j] == -1表示遞迴過程p(i,j)計算過,但返回值是0。如果map[i][j]的值既不等於0,也不等於-1,記為a,則表示遞迴過程p(i, j)的返回值為a。

#include

#include

using

namespace

std;

int **map;

int process2(vector

arr, int index, int aim, int **map)

else

else

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

if(res == 0)

map[index][aim] = 0;

else

map[index][aim] = res;

return res;

}int coins2(vector

arr, int aim)

int main()

; cout

<<"coins1(arr, 1) = "

<0])<<"coins1(arr, 15) = "

<1])<<"coins1(arr, 1000) = "

<2])0;}

這個優化步驟就是記憶化搜尋。它是暴力遞迴的最初級的優化技巧,分析遞迴函式的狀態可以由哪些變數表示,做出相應維度和大小的map即可。記憶化搜尋方法的時間複雜度為o(n*aim^2)。此題還可以運用動態規劃的思想和空間壓縮的技巧再去進行進一步的優化!

換錢的方法數

1.給定陣列arr,arr中所有的值都為正數且不重複。每個值代表一種面值的貨幣,每種貨幣都可以使用任意張,再給定乙個整數aim代表要找的錢數,求組成aim的最少貨幣數。方法一 暴力遞迴 遞迴部分 a index i就是用幾張a index process a,0,target defprocess ...

換錢的方法數

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

換錢的方法數

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