程序員面試金典(動態規劃):1分,5分,10分,25分硬幣面值組合問題(解題思路)

問題描述:

假設我們有8種不同面值的硬幣{1,2,5,10,20,50,100,200},用這些硬幣組合夠成一個給定的數值n。例如n=200,那麼一種可能的組合方式爲 200 = 3 * 1 + 1*2 + 1*5 + 2*20 + 1 * 50 + 1 * 100. 問總過有多少種可能的組合方式?

[華爲面試題] 1分2分5分的硬幣三種,組合成1角,共有多少種組合?

[創新工廠筆試題] 有1分,2分,5分,10分四種硬幣,每種硬幣數量無限,給定n分錢,有多少中組合可以組成n分錢?

解題思路:

給定一個數值sum,假設我們有m種不同類型的硬幣{V1, V2, ..., Vm},如果要組合成sum,那麼我們有

sum = x1 * V1 + x2 * V2 + ... + xm * Vm 

求所有可能的組合數,就是求滿足前面等值的係數{x1, x2, ..., xm}的所有可能個數。

[思路1] 當然我們可以採用暴力枚舉,各個係數可能的取值無非是x1 = {0, 1, ..., sum / V1}, x2 = {0, 1, ..., sum/ V2}等等。這對於硬幣種類數較小的題目還是可以應付的,比如華爲和創新工廠的題目,但是複雜度也很高O(sum/V1 * sum/V2 * sum/V3 * ...)

[思路2] 從上面的分析中我們也可以這麼考慮,我們希望用m種硬幣構成sum,根據最後一個硬幣Vm的係數的取值爲無非有這麼幾種情況,xm分別取{0, 1, 2, ..., sum/Vm},換句話說,上面分析中的等式和下面的幾個等式的聯合是等價的。

sum = x1 * V1 + x2 * V2 + ... + 0 * Vm

sum = x1 * V1 + x2 * V2 + ... + 1 * Vm

sum = x1 * V1 + x2 * V2 + ... + 2 * Vm

...

sum = x1 * V1 + x2 * V2 + ... + K * Vm  

  其中K是該xm能取的最大數值K = sum / Vm。可是這又有什麼用呢?不要急,我們先進行如下變量的定義:

dp[i][sum] = 用前i種硬幣構成sum 的所有組合數。

  那麼題目的問題實際上就是求dp[m][sum],即用前m種硬幣(所有硬幣)構成sum的所有組合數。在上面的聯合等式中:當xn=0時,有多少種組合呢? 實際上就是前i-1種硬幣組合sum,有dp[i-1][sum]種! xn = 1 時呢,有多少種組合? 實際上是用前i-1種硬幣組合成(sum - Vm)的組合數,有dp[i-1][sum -Vm]種; xn =2呢, dp[i-1][sum - 2 * Vm]種,等等。所有的這些情況加起來就是我們的dp[i][sum]。所以:

dp[i][sum] = dp[i-1][sum - 0*Vm] + dp[i-1][sum - 1*Vm]

+ dp[i-1][sum - 2*Vm] + ... + dp[i-1][sum - K*Vm]; 其中K = sum / Vm

換一種更抽象的數學描述就是:

遞歸公式

  通過此公式,我們可以看到問題被一步步縮小,那麼初始情況是什麼呢?如果sum=0,那麼無論有前多少種來組合0,只有一種可能,就是各個係數都等於0;

dp[i][0] = 1   // i = 0, 1, 2, ... , m

  如果我們用二位數組表示dp[i][sum], 我們發現第i行的值全部依賴與i-1行的值,所以我們可以逐行求解該數組。如果前0種硬幣要組成sum,我們規定爲dp[0][sum] = 0. 

求解實際問題

題目描述

有數量不限的硬幣,幣值爲25分、10分、5分和1分,請編寫代碼計算n分有幾種表示法。

給定一個int n,請返回n分有幾種表示法。保證n小於等於100000,爲了防止溢出,請將答案Mod 1000000007。

class Coins {
public:
    int countWays(int n) {
        // write code here
        int coins[4]={1,5,10,25};
        int dp[100001] = {0};       
        dp[0] = 1;
        for(int i = 0;i < 4;++i){
            for(int j = coins[i];j <= n;++j){
                dp[j] =(dp[j]+dp[j-coins[i]])%1000000007;               
            }
        }
        return dp[n];
    }
};



發佈了29 篇原創文章 · 獲贊 9 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章