EOJ1488 COIN COLLECTOR 贪心

来源:互联网 发布:ibm软件测试招聘 编辑:程序博客网 时间:2024/06/06 13:15

和舍友打了一个半小时嘴炮,终于搞定了这道题,很开心。本题思路就是构造一个答案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;}


0 0