hdu5527 Too Rich


题目描述:

Too Rich

Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 169 Accepted Submission(s): 50

Problem Description
You are a rich person, and you think your wallet is too heavy and full now. So you want to give me some money by buying a lovely pusheen sticker which costs p dollars from me. To make your wallet lighter, you decide to pay exactly p dollars by as many coins and/or banknotes as possible.

For example, if p=17 and you have two 10coins,four 5 coins, and eight 1coins,youwillpayitbytwo 5 coins and seven $1 coins. But this task is incredibly hard since you are too rich and the sticker is too expensive and pusheen is too lovely, please write a program to calculate the best solution.

Input
The first line contains an integer T indicating the total number of test cases. Each test case is a line with 11 integers p,c1,c5,c10,c20,c50,c100,c200,c500,c1000,c2000, specifying the price of the pusheen sticker, and the number of coins and banknotes in each denomination. The number ci means how many coins/banknotes in denominations of i dollars in your wallet.

1≤T≤20000
0≤p≤109
0≤ci≤100000

Output
For each test case, please output the maximum number of coins and/or banknotes he can pay for exactly p dollars in a line. If you cannot pay for exactly p dollars, please simply output ‘-1’.

Sample Input
3
17 8 4 2 0 0 0 0 0 0 0
100 99 0 0 0 0 0 0 0 0 0
2015 9 8 7 6 5 4 3 2 1 0

Sample Output
9
-1
36

题解:

很好的一道性质贪心题,比赛时想了一半没想全真是笨…
首先发现钱的规律,好像前面的都是后面的约数,因为我们要凑p的钱,那么感觉上从后往前贪心,能够用就狂用,因为后面的不管怎么凑都是要达到一下这个数的.
但是小心,上面的那个贪心是不对的.对于1 5 10 20 目前来说对,但是1 5 10 20 50 100,的话, 100那个点就不对了,注意到20和50虽然都是100的约数,但是20不是50的约数,前面的不一定能够凑出100,比如20*3+50.于是发现不行,但是无所谓.因为我们发现只要后一个是前一个的倍数就可以了,而给的钱特殊的只有20和50 以及 200和500这两组. 我们这样处理:
枚举50用了奇数还是偶数个,因为偶数个50可以当做权值为2的100来用,满足我们的条件,而奇数的话,我们在计算之前就把这个50减掉就好. 200和500同样. ——————这是这一道题很重要的一个性质.
还有一个条件,就是用尽量多的硬币,我们上面搞是用尽量少的硬币,怎么办?其实就是反向思维.我们凑剩下的钱数,那么就是用尽量少的啦:)

另外,金爷教的正向做的方法.
正向做,就是尽量多的使用硬币.从一块开始用,能用尽量用,然后发现之后能继续凑的都是5的倍数了,那么我们要求尽量用1的结果后再使剩下的数是5的倍数,不行的话就减少1的个数. 一直这样搞.
特殊的有两个地方:
(1)20和50之前的那个10不知道要凑20或50的倍数,仍然利用暴力枚举50奇偶的方法来搞.
(2)有可能比如说没办法退5来使得是10的倍数,因为5本来就用的只是0个,那么其实应该退5个1来搞.

重点:

(1)逆向凑的话,全是前一个是后一个的约数的才能贪心.利用奇偶的暴力来搞
(2)逆向思维搞出最少.
(3)正向的话,一直用最多硬币这个贪心,并在满足可以凑成p的前提下没办法的调整.

代码:
//代码是逆向做的
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>

using namespace std;

typedef long long ll;

const int maxn = 20;
const int INF = 1e9;
const int N = 10;
int vis[maxn];
int val[maxn], have[maxn], f[maxn], p,allnum, allval;
int ans;

int gao(int p, int f[], int ans)
{
    for(int i = N-1;i>=0;i--)
    {
        int t = f[i];
        t = min(t, p/val[i]);
        if(i==4 || i==7)
        {
            if(t%2==1)
                t--;
        }
        p -= t*val[i];
        ans += t;
    }
    if(p!=0)
        return -1;
    return ans;
}

void solve()
{
    int result = -1;
    allnum = 0;
    allval = 0;
    for(int i = 0; i<N; i++)
    {
        allnum += have[i];
        allval += have[i]*val[i];
    }
    p = allval - p;
    if(p<0)
    {
        printf("-1\n");
        return;
    }
    for(int sta = 0;sta<(1<<2);sta++)
    {
        int pp = p, tmp=0;
        for(int i = 0;i<N;i++)
            f[i] = have[i];
        if(((1<<0)&sta)&&have[4]>=1&&pp>=50)
        {
            pp -= 50;
            f[4]--;
            tmp++;
        }
        if(((1<<1)&sta)&&have[7]>=1&&pp>=500)
        {
            pp -= 500;
            f[7]--;
            tmp++;
        }
        int t = gao(pp, f, tmp);
        if(t!=-1)
            result = max(result, allnum - t);
    }
    printf("%d\n", result);
}

int main()
{
   // freopen("Ain.txt", "r" ,stdin);
    val[0] = 1;
    val[1] = 5;
    val[2] = 10;
    val[3] = 20;
    val[4] = 50;
    val[5] = 100;
    val[6] = 200;
    val[7] = 500;
    val[8] = 1000;
    val[9] = 2000;
    int ncase;
    scanf("%d", &ncase);
    while(ncase--)
    {
        scanf("%d", &p);
        for(int i = 0; i<N; i++)
            scanf("%d", &have[i]);
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值