uva 166 Making Change

來源:互聯網
上載者:User

原題:
166 Making Change
Given an amount of money and unlimited (almost) numbers of coins we know that an amount of money may be made up in a variety of ways. A more interesting problem arises when goods are bought and need to be paid for, with the possibility that change may need to b e given. Given the finite resources of most wallets nowadays, we are constrained in the number of ways in which we can make up an amount to pay for our purchases —-assuming that we can make up the amount in the first place, but that is another story .
The problem we will be concerned with will be to minimise the number of coins that change hands
at such a transaction, given that the shopkeeper has an adequate supply of all coins. (The set of New Zealand coins comprises 5c, 10c, 20c, 50c, ¥ 1 and ¥ 2.) Th us if w e need to pay 55c, and we do not hold a 50c coin, we could pay this as 2*20c + 10c + 5c to make a total of 4 coins. If we tender ¥1 w e will receive 45c in change which also involv es 4 coins, but if we tender ¥ 1.05 (¥ 1 + 5c), we get 50c change and the total number of coins that changes hands is only 3.
Write a program that will read in the resources a vailable to you and the amount of the purchase and will determine the minim um n um b er of coins that c hange hands.
Input
Input will consist of a series of lines, each line defining a diffierent situation. Each line will consist of 6 in tegers representing the numbers of coins a vailable to you in the order given above, followed by a real number representing the value of the transaction, which will always be less than ¥ 5.00. The file will be terminated by six zero as (0 0 0 0 0 0). The total value of the coins will alw ays be sufficient to mak e up the amount and the amount will always be achievable, that is it will always be a multiple of 5c.
Out put
Output will consist of a series of lines, one for each situation defined in the input. Each line will consist of the minimum number of coins that change hands right justified in a field 3 characters wide.
Sample input
2 4 2 2 1 0 0.95
2 4 2 0 1 0 0.55
0 0 0 0 0 0
Sample out put
2
3
中文:
找零錢問題,現在你手上有5c、10c、20c、50c、¥1、¥2的硬幣(1¥=100c),給你這些硬幣在你錢包裡的數量,然後再給你一個不超過5¥的數表示你要付款的金額,問你在交易過程當中使用的硬幣的個數最少是多少個。這裡面,比如你要交易的金額是15c但是你手頭沒有10c和5c,你可以給售貨員一個20c,然後售貨員找你5c,那麼這樣一共用了2個硬幣。(假設售貨員手裡有無數的各種硬幣)

#include <bits/stdc++.h>using namespace std;const int inf=999999;int money[6]={1,2,4,10,20,40};int num[6];int change[3001];int give[3001];double n;int main(){    ios::sync_with_stdio(false);    memset(change,0,sizeof(change));    for(int j=1;j<3001;j++)    {        int tmp=j,cnt=0;        for(int i=5;i>=0;i--)        {            if(tmp>=money[i])            {                cnt+=(tmp/money[i]);                tmp-=(money[i]*(tmp/money[i]));                if(tmp==0)                    break;            }        }        change[j]=cnt;    }    while(cin>>num[0]>>num[1]>>num[2]>>num[3]>>num[4]>>num[5]>>n)    {        if(num[0]+num[1]+num[2]+num[3]+num[4]+num[5]==0)            return 0;        for(int i=0;i<3001;i++)            give[i]=inf;        give[0]=0;        int x=(n+0.005)*100.0;        x/=5;//      cout<<x<<endl;        int tot=0;        for(int i=0;i<6;i++)            tot+=num[i]*money[i];//      cout<<tot<<" "<<x<<endl;        for(int i=0;i<6;i++)        {            for(int j=1;j<=num[i];j++)            {                for(int k=tot;k>=money[i]*j;k--)                {                    give[k]=min(give[k],give[k-money[i]*j]+j);                }            }        }        int ans=give[x];        for(int i=x+1;i<=100;i++)        {            ans=min(ans,give[i]+change[i-x]);        }        cout<<setw(3)<<ans<<endl;    }    return 0;}

解答:
比較真實的找零錢問題,呵呵。首先,然後把5c、10c、20c、50c、¥1、¥2變成5,10,20,50,100,200,再把輸入的付款數變成整數,這裡注意轉換的時候精度有丟失,需要加上0.005再乘以100.0。由於告訴你所有的金額都是5的倍數,那麼可以用一下簡單的小離散化的技巧,把所以數值都除以5。那麼硬幣的金額就可以變成1,2,4,10,20,40,付款數也要除以5。
首先想一下原始的那個換硬幣的問題,給你一個數值,問你有多少種換硬幣的方法。相當於是一個完全背包,硬幣的面值是體積,硬幣的個數就是價值。顯然,每個硬幣的價值都是1.
現在給你每種硬幣對應的個數,那麼就可以考慮成一個多重背包問題。如果只簡單考慮用自己身上有的錢來湊付款數的話,那麼按照多重背包的解法求最少用多少硬幣可以寫出這樣的轉移方程
dp[j]=min(dp[j],dp[j-money[i]×k]+k (k≤money[i]的數量)
但是,可能會出現手裡的幾種硬幣不夠使的情況,比如要支付15,你手裡的5和10的硬幣都是0個,可是你有一個20的硬幣,那麼可以給售貨員20c然後售貨員找你5c。
這種情況就不滿足上面的那個轉移方程了,因為要算15的最少找錢數,設定dp[15],轉移方程只能找前面的狀態,也就是只能找小於15的狀態,不能找大於15的。
一般這種情況有三種解決方案
1.增加一個緯度來描述目前狀態,比如dp[15][i]就可以找dp[20][i-k]之類的
2.找等價代換,或者改變狀態值,比如給多給售貨員錢,然後讓售貨員找零,實際上就是售貨員收到你的錢後把應該找你的錢按照使用硬幣最少的方式找給你
3.記憶化搜尋,從後往前推
第一種方法畫個二維表格,發現不好使
第二種方法,考慮一下付款55c的找錢方式,首先可以自己湊,也可以給出多餘55的錢,讓售貨員找零錢。那麼,可以轉移到55的狀態就是從55到你身上所有錢的總數,因為,你可以把你身上的錢都給售貨員,然後讓售貨員找零錢,當然,結果比一定是最優的。

所以可以這樣做,首先計算出身上所有的錢數tot,然後用多重背包的方法算出give[tot]到give[55]的所有值。表示給出55到tot時,僅用自己身上的錢能給出的最小數目,給不出的用inf值放進去。先用自己身上的零錢湊55,如果能湊出來,找出可以湊的最小值,儲存到ans當中,如果湊不出來,那麼枚舉大於55值i,表示給售貨員的錢最少用多少硬幣,用give表示,然後用i-55表示售貨員找你的錢的最小硬幣數用change表示。give和change的和表示這次交易用最小硬幣數。
那麼ans=min(ans,give+change)即可。
可以先把售貨員找錢數打個表,由於售貨員的各種硬幣數有無限個,貪心方法就能解決售貨員找錢的最小數值。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.