POJ 3040 Allowance 贪心

题目链接: poj 3040

贪心
看到题目的时候完全没有什么思路,这类题目做的还是太少了,完全是面向题解编程。

我的(bierende)思路:第一时间肯定是想到浪费的越少越好。
1. 当币值大于C的时候,就直接用这个硬币付工资。
2. 当币值小于C的时候,就要想一种搭配方案,使得总的价值等于或者多于C但多余的钱要尽可能少,这个就比较难想到了。

而题目中有一个条件是,大的币值能被小的整除,所以若干个小的一定能凑出大的,那么可以用一个大的或若干个小的的情况下,肯定是要选用一个大的,这样之后的搭配方案才能更好的符合”超出C的钱尽可能少“这个条件。

所以, 当当币值小于C的时候,从币值最大的开始遍历,一个小于C,那两个、三个一直到n个,直到刚好等于C或者n+1个硬币大于C,如果是后者,那就选n个,在从币值更小的硬币重复上面的过程,直到刚好等于C或者全部硬币遍历完
如果最后的全部遍历完还是不能凑到刚好等于C,那么从小到大,直到找到一个加上这个硬币刚好大于C的。如果找不到,就说明剩下的全部加起来已经没有C了,结束了

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>

using namespace std;

const int MAXN = 25;
pair<int,int> p[MAXN];//存储硬币币值和数量
int n, c;

int main()
{
    while(scanf("%d%d", &n, &c) == 2)
    {
        for(int i=0; i<n; i++)
            scanf("%d%d", &p[i].first, &p[i].second);
        sort(p, p+n);//按币值从小到大排序
        int ans = 0, require, need[MAXN], rest;
        while(true)
        {
            rest = c;
            memset(need, 0, sizeof(need));
            for(int i=n-1; i>=0; --i)//从大到小选择
            {
                if(p[i].second>0)
                {
                    require = min(p[i].second, rest/p[i].first);//需要这个币值的硬币的数量
                    rest -= require*p[i].first;
                    need[i] = require;
                }
                if(rest == 0) break;//刚好凑出C
            }
            if(rest)
            {
                for(int i=0; i<n; i++)//从小到大选择
                {
                    if(p[i].second && p[i].first>=rest)//加上这个硬币(最小)就刚好大于C
                    {
                        rest = 0;
                        need[i]++;
                        break;
                    }
                }
            }
            if(rest) break;//两轮选择后仍然凑不齐C,结束
            int week = 1e8;//用这个方案能发多少个星期的工资
            for(int i=0; i<n; i++)
            {
                if(need[i]) week = min(week, p[i].second/need[i]);
            }
            ans+=week;
            for(int i=0; i<n; i++)//把用掉的硬币减去
            {
                if(need[i]) p[i].second -= week*need[i];
            }
        }
        cout << ans << endl;
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值