uva 166 Making Change

原题:
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)即可。
可以先把售货员找钱数打个表,由于售货员的各种硬币数有无限个,贪心方法就能解决售货员找钱的最小数值。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值