POJ 3040 Allowance 贪心

很厉害的题目,没看题解之前一直忽略了题目中的一个性质,下一个硬币面额总是上一个硬币面额的倍数。我也不清楚如果看到了这一点之后我会不会想出答案。

这道题目最优策略要考虑两个因素:

1.取得相同面额时,硬币数量越少越好

2.相同硬币数量时,面额越少越好(首先总面额要大于C)

这个限制条件是很难满足的,我自己想的一些贪心策略都可以举反例证伪(举得反例有一些是不符合倍数要求的)。

但是在题目特殊的倍数要求下,这个条件就变得可以满足了。

贪心策略如下,证明本人目前还没想出来:

1.先按面值从小到大排序

2.从后往前取,在保证总面额小于等于C的前提下尽量多取

3.从前往后取,直到取得总面额大于等于C

很奇妙的是,这个贪心策略加上题目特殊的倍数要求,就变得总是正确的了,里面的证明肯定很精彩,以后如果我想起来了再补充吧。

代码如下:

#include <map>
#include <queue>
#include <cstdio>
#include <vector>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define MAX_N 10005

using namespace std;

struct data
{
    int value;
    int number;
    bool operator < (const data& b) const
    {
        return value < b.value;
    }
};
typedef long long int ll;

int main()
{
    //freopen("in.txt", "r", stdin);

    int N, C;
    data dat[MAX_N];
    while (~scanf("%d%d", &N, &C))
    {
        for (int i = 0; i < N; i++)
            scanf("%d%d", &dat[i].value, &dat[i].number);
        sort(dat, dat + N);

        ll ans = 0;
        for (int i = N - 1; i >= 0; i--)//大于C的硬币先处理
        {
            if (dat[i].value >= C)
            {
                ans += dat[i].number;
                dat[i].number = 0;
            }
            else
                break;
        }

        while (1)
        {
            int need = C;
            for (int i = N - 1; i >= 0; i--)
            {
                if (dat[i].number && need >= dat[i].value)
                {
                    int k = need / dat[i].value;
                    k = min(k, dat[i].number);
                    need -= k * dat[i].value;
                    dat[i].number -= k;
                }
            }

            for (int i = 0; i <= N - 1; i++)
            {
                if (dat[i].number && need)
                {
                    int k;
                    if (need % dat[i].value)
                        k = (need + dat[i].value) / dat[i].value;
                    else
                        k = need / dat[i].value;
                    k = min(k, dat[i].number);
                    if (need >= k * dat[i].value)
                        need -= k * dat[i].value;
                    else
                        need = 0;
                    dat[i].number -= k;
                }
            }

            if (need == 0)
                ans++;
            else
                break;
        }

        printf("%lld\n", ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值