EOJ1488 COIN COLLECTOR 贪心

和舍友打了一个半小时嘴炮,终于搞定了这道题,很开心。本题思路就是构造一个答案a0,a1,a2......     ai是找零的硬币。而需要找零的总钱数就是a0+a1+a2......


贪心策略:


1.因为题目要求是获得最多种未曾获得的硬币,所以我们最开始的想法是将所有没获得的硬币都放进答案中。但是这样真的对吗?假如输入是1 0, 2 0, 3 0。那么找零为6时,1,2,3不会都用到。因为 1 + 2 >= 3,所以在经过3找零后,需要找零的钱<=2,所以1,2至多有一个数被用到。这启发我们假如已经构造了局部最优答案a,b。想要构造更大的答案a,b,c时。当a + b >= c,a,b,c三个数不可能同时用到。所以这个时候我们就要开始删除一个数了,咋删呢?看策略2。


2.假如删除c,因为a + b >= c所以在找零的时候必然会先被c找零,所以这种方法是错误的。那么我们就要从a,b中间删一个,根据题意要使得找零的钱数最少,所以我们删掉a,b中较大的那个b.


3.假如c硬币收藏家已经有了,从答案中删除c


hint:有人可能会想到这样一个问题,我们从答案a,b,c向a,b,c,d拓展时,假如a+b+c>=d,我们要删除c,那么会不会剩下的a+b仍然大于d呢?即我们要连续删除多个数直至前面的和小于后面。答案是否定的!!最多删除一个数。假如a+b+c>=d 且 a+b>=d。由于a,b,c是已经构造好的答案,所以还有a+b<c。我们得出c>a+b>=d,即c>d的荒谬结论,c面值肯定小于d,因为我们是按面值从小到大拓展的。


举个例子:输入是

7 25

1 0

2 0

3 1

5 0

10 0

13 0

20 0

先构造答案1 2

构造1 2 3

发现1 + 2 >= 3

删除2

又因为3已经有了

删除3

构造的答案就是1

再构造1 5

再构造1 5 10

因为1 + 5 < 10所以可用

再构造1 5 10 13

因为1 + 5 + 10 >= 13

所以删除10

因为13没有

所以答案是1 5 13

再构造 1 5 13 20

发现1 + 5 + 13 + 20 > k - 1超出限制

所以删掉20

即最后的答案就是 1 5 13

收藏家需要花的钱是k - (1 + 5 + 13) = 6

具体代码如下:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <sstream>
#include <fstream>
#include <istream>
#include <ostream>
#include <complex>
#include <cstring>
#include <utility>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <string>
#include <cctype>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <list>
#include <new>
#include <set>
#include <map>
 
using namespace std;
 
const int maxn = 500000 + 5;
const int INF = 0x3f3f3f3f;
typedef long long int LL;
typedef vector<LL> vec;
 
vec ans;//保存答案
 
int main()
{
    //freopen("1.txt", "r", stdin);
    LL n, k;
    scanf("%I64d%I64d", &n, &k);
    LL sum = 0;
    for (int i=0; i<n; i++)
    {
        LL p, has;
        scanf("%I64d%I64d", &p, &has);
        if (sum >= p)//要删去上次答案的最后一项
        {
            sum -= ans[ans.size() - 1];
            ans.pop_back();
        }
        if (!has)//如果当前硬币收藏家没有,则加入答案中
        {
            if (sum + p > k - 1)
                break;
            ans.push_back(p);
            sum += p;
        }
    }
    if (sum == 0)//如果硬币收藏家全有,则找零的钱为1
        sum = 1;
    cout << ans.size() << endl << k - sum << endl;
    return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值