日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

硬币面值组合

發布時間:2023/12/13 综合教程 49 生活家
生活随笔 收集整理的這篇文章主要介紹了 硬币面值组合 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

【轉】http://www.hawstein.com/posts/8.7.html

題目

原文:

Given an infinite number of quarters (25 cents), dimes (10 cents), nickels (5 cents) and pennies (1 cent), write code to calculate the number of ways of representing n cents.

譯文:

我們有25分,10分,5分和1分的硬幣無限個。寫一個函數計算組成n分的方式有幾種?

解答

一開始,我覺得使用遞歸不斷地累加四種幣值的硬幣,當累加到n分,組成方式就加1。 最后就能得到一共有多少種組合方式,聽起來挺正確的,然后寫了以下代碼:

int cnt = 0;
void sumn(int sum, int n){
    if(sum >= n){
        if(sum == n) ++cnt;
        return;
    }
    else{
        sumn(sum+25, n);
        sumn(sum+10, n);
        sumn(sum+5, n);
        sumn(sum+1, n);
    }
}

但,這是錯誤的。問題出在哪?有序與無序的區別!這個函數計算出來的組合是有序的, 也就是它會認為1,5和5,1是不一樣的,導致計算出的組合里有大量是重復的。 那要怎么避免這個問題?1,5和5,1雖然會被視為不一樣,但如果它們是排好序的, 比如都按從大到小排序,那么就都是5,1了,這時就不會重復累計組合數量。 可是我們總不能求出答案來后再搞個排序吧,多費勁。這時我們就可以在遞歸上做個手腳, 讓它在計算的過程中就按照從大到小的幣值來組合。比如, 現在我拿了一個25分的硬幣,那下一次可以取的幣值就是25,10,5,1;如果我拿了一個 10分的,下一次可以取的幣值就只有10,5,1了;這樣一來,就能保證,同樣的組合, 我只累計了一次,改造后的代碼如下:

int cnt = 0;
void sumN(int sum, int c, int n){
    if(sum >= n){
        if(sum == n) ++cnt;
        return;
    }
    else{
        if(c >= 25)
            sumN(sum+25, 25, n);
        if(c >= 10)
            sumN(sum+10, 10, n);
        if(c >= 5)
            sumN(sum+5, 5, n);
        if(c >= 1)
            sumN(sum+1, 1, n);
    }
}

有個全局變量總覺得看起來不好看,于是我們可以把這個全局變量去掉, 讓函數來返回這個組合數目,代碼如下:

int sum_n(int sum, int c, int n){
    int ways = 0;
    if(sum <= n){
        if(sum == n) return 1;
        if(c >= 25)
            ways += sum_n(sum+25, 25, n);
        if(c >= 10)
            ways += sum_n(sum+10, 10, n);
        if(c >= 5)
            ways += sum_n(sum+5, 5, n);
        if(c >= 1)
            ways += sum_n(sum+1, 1, n);
    }
    return ways;
}

CTCI原文中給出的解法如下:

int make_change(int n, int denom){
    int next_denom = 0;
    switch(denom){
    case 25:
        next_denom = 10;
        break;
    case 10:
        next_denom = 5;
        break;
    case 5:
        next_denom = 1;
        break;
    case 1:
        return 1;
    }
    int ways = 0;
    for(int i=0; i*denom<=n; ++i)
        ways += make_change(n-i*denom, next_denom);
    return ways;
}

也是從幣值大的硬幣開始,每次取i個(i乘以幣值要小于等于n), 然后接著去取幣值比它小的硬幣,這時就是它的一個子問題了,遞歸調用。 具體來怎么來理解這個事呢?這樣,比如我要湊100分,那我先從25分開始, 我先取0個25分,然后遞歸調用去湊100分;再然后我取1個25分,然后遞歸調用去湊100-25 =75分;接著我取2個25分,遞歸調用去湊100-25*2=50分……這些取了若干個 25分然后再去遞歸調用,取的就是10分了。一直這樣操作下去,我們就會得到, 由若干個25,若干個10分,若干個5分和若干個1分組成的100分,而且, 這里面每種幣值的數量都可以為0。

完整代碼如下:

#include <iostream>
using namespace std;

int cnt = 0;
void sumN(int sum, int c, int n){
    if(sum >= n){
        if(sum == n) ++cnt;
        return;
    }
    else{
        if(c >= 25)
            sumN(sum+25, 25, n);
        if(c >= 10)
            sumN(sum+10, 10, n);
        if(c >= 5)
            sumN(sum+5, 5, n);
        if(c >= 1)
            sumN(sum+1, 1, n);
    }
}
int sum_n(int sum, int c, int n){
    int ways = 0;
    if(sum <= n){
        if(sum == n) return 1;
        if(c >= 25)
            ways += sum_n(sum+25, 25, n);
        if(c >= 10)
            ways += sum_n(sum+10, 10, n);
        if(c >= 5)
            ways += sum_n(sum+5, 5, n);
        if(c >= 1)
            ways += sum_n(sum+1, 1, n);
    }
    return ways;
}
int make_change(int n, int denom){
    int next_denom = 0;
    switch(denom){
    case 25:
        next_denom = 10;
        break;
    case 10:
        next_denom = 5;
        break;
    case 5:
        next_denom = 1;
        break;
    case 1:
        return 1;
    }
    int ways = 0;
    for(int i=0; i*denom<=n; ++i)
        ways += make_change(n-i*denom, next_denom);
    return ways;
}
int main(){
    int n = 10;
    sumN(0, 25, n);
    cout<<cnt<<endl;
    cout<<make_change(n, 25)<<endl;
    cout<<sum_n(0, 25, n)<<endl;
    return 0;
}

總結

以上是生活随笔為你收集整理的硬币面值组合的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。