poj1787 Charlie‘s Change

Charlie is a driver of Advanced Cargo Movement, Ltd. Charlie drives a lot and so he often buys coffee at coffee vending machines at motorests. Charlie hates change. That is basically the setup of your next task.

Your program will be given numbers and types of coins Charlie has and the coffee price. The coffee vending machines accept coins of values 1, 5, 10, and 25 cents. The program should output which coins Charlie has to use paying the coffee so that he uses as many coins as possible. Because Charlie really does not want any change back he wants to pay the price exactly.

Input

Each line of the input contains five integer numbers separated by a single space describing one situation to solve. The first integer on the line P, 1 <= P <= 10 000, is the coffee price in cents. Next four integers, C1, C2, C3, C4, 0 <= Ci <= 10 000, are the numbers of cents, nickels (5 cents), dimes (10 cents), and quarters (25 cents) in Charlie's valet. The last line of the input contains five zeros and no output should be generated for it.

Output

For each situation, your program should output one line containing the string "Throw in T1 cents, T2 nickels, T3 dimes, and T4 quarters.", where T1, T2, T3, T4 are the numbers of coins of appropriate values Charlie should use to pay the coffee while using as many coins as possible. In the case Charlie does not possess enough change to pay the price of the coffee exactly, your program should output "Charlie cannot buy coffee.".

Sample

InputcopyOutputcopy
12 5 3 1 2
16 0 0 0 1
0 0 0 0 0
Throw in 2 cents, 2 nickels, 0 dimes, and 0 quarters.
Charlie cannot buy coffee.

 题目大意:对于p元的饮料,查理想要花尽可能多的硬币来购买到该饮料对于1,5,10,25的硬币各有 C1, C2, C3, C4个,输入为p C1, C2, C3, C4。输出需要用什么硬币买到即可

变形的背包的问题,该题的难点为如何追溯或记录硬币的使用个数,以及对于j金额凑不出来时如何特判然后不处理

思路:对于不可被凑出的金额我们把他标记成-1;硬币数量使用的话可以用一个二维数组use[i][j]来记录凑出j元时第i种硬币的使用数量;然后从dp[0]=0开始递推,由于要用尽可能多的硬币

故递推公式为

if(dp[j - c[i].w] != -1)dp[j] =max( dp[j - c[i].w] + 1,dp[j]);

更详细思路看代码注释

具体ac代码如下:

#include <iostream> //第二周D题
#include <cstring>
using namespace std;
struct node
{
    int w, co;//w,co用来存储硬币的价值于数量
};
int dp[10005];
int use[5][10005]; // use[i][j]用来记录当j元能被凑齐时第i种硬币最多用几枚
node c[5];
int main()
{
    // #ifdef Online_Judge
    //     freopen("INPUT.in", "r", stdin);
    // #endif
    c[1].w = 1;
    c[2].w = 5;
    c[3].w = 10;
    c[4].w = 25;
    int p;
    while (cin >> p >> c[1].co >> c[2].co >> c[3].co >> c[4].co)
    {
        if (c[1].co + c[2].co + c[3].co + c[4].co + p == 0)
            break;
        for (int i = 0; i <= p; i++)
            dp[i] = -1; //初始化数组,用-1来表示这个金额是达不到的,无意义的
        dp[0] = 0;      //从0开始递推
        memset(use, 0, sizeof use);
        for (int i = 1; i <= 4; i++)
        {
            int sum = 0;
            for (int k = i; k > 0; k--)//sum用来记录前i种硬币最多可以凑出多少钱
                sum += c[k].w * c[k].co;//对于第i种硬币最多为数量乘以价值的钱
            for (int j = 1; j <= p; j++)
            {
                if (c[i].w > j) //如果对于i硬币一个都放不下那就没意义
                    continue;
                if (c[i].co > 0) //必须有这种货币才有的说
                {
                    //如果对于这种硬币取的解法更优;如果这个硬币还有的取;并且对于j - c[i].w金额也可以凑出来的话就进行优化
                    if (dp[j - c[i].w] + 1 > dp[j] && use[i][j - c[i].w] + 1 <= c[i].co && dp[j - c[i].w] != -1)
                    {
                        dp[j] = dp[j - c[i].w] + 1;
                        use[i][j] = use[i][j - c[i].w] + 1;
                        for (int k = i - 1; k > 0; k--)
                        {//记录好对于前i种硬币j金额凑出来是具体是用了哪些硬币凑出来的
                            use[k][j] = use[k][j - c[i].w];
                        }
                        if (j == sum)//达到可凑出的最大金额了,退出
                            break;
                    }
                }
            }
        }
        if (dp[p] == -1)
        {
            printf("Charlie cannot buy coffee.\n");
            continue;
        }
        printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n", use[1][p], use[2][p], use[3][p], use[4][p]);
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值