【ZOJ3916 2016年浙大2月月赛B】【贪心 STL-SET】Buy Cakes n蛋糕k张折扣券蛋糕双价最多能买蛋糕数

  • Post author:
  • Post category:其他




Buy Cakes




Time Limit:

2 Seconds

Memory Limit:

65536 KB


Andy is always a hungry man. He never stopped eating something sweet, expecially cakes.

Yesterday his mother gave him some pocket money, and now, he has

M

dollars all together.

The cake shop provides

N

pieces of cakes, and each of them costs

a

i


dollars respectively.

Fortunately, Andy has

K

discount coupons for that cake shop. When we use a coupon, we can buy a cake at a lower prize of

b

i


dollars instead of the original

a

i


dollars.

So, Andy wants you to help him find out how many cakes he can buy at most.

Input

There are multiple cases.

The first line contains the number of cases for this problem

T

(1<=T<=10).

For each case, the first line contains three integers

N

(1<=N<=10

5

),

K

(0<=K<=N), and

M

(0<=M<=10

14

).

For next

N

lines, each line has two integers

a

i


and

b

i


(1<=b

i

<=a

i

<=10

9

).

Output

For each case, output the number of cakes he can get at most.

Sample Input

2
4 1 7 
3 2 
2 2  
8 1  
4 3 
2 0 3
5 4
6 3

Sample Output

3
0

Hint

One of the solution for first case of sample input is to choose candy 1, 2, 3, and use the coupon on candy 3.

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 1e5 + 10, M = 0, Z = 1e9 + 7, ms63 = 0x3f3f3f3f;
int casenum, casei;
int n, k;
LL m;
struct A
{
	int x, y ,d;
	int o;
	bool operator < (const A& b)const
	{
		if (y != b.y)return y < b.y;
		else return d > b.d;
	}
}a[N], b[N];
bool cmp(A a, A b)
{
	return a.x < b.x;
}
multiset<int>sot;
bool buy[N];

//这是比赛的时候写的数据制造对拍程序
int c[20];
int NUM;
void RANDOM()//注意:"random"会与一些编译器下的标识符冲突
{
	n = rand() % 8 + 1;
	k = rand() % (n+1);
	m = rand() % 50;
	for (int i = 1; i <= n; ++i)
	{
		a[i].x = rand() % 10;
		a[i].y = rand() % (a[i].x + 1);
	}
	for (int i = 1; i <= n; ++i)c[i] = i;
	NUM = 0;
	do
	{
		int mm = m;int num = 0;
		for (int i = 1; i <= k; ++i)if (mm >= a[c[i]].y) {
			mm -= a[c[i]].y;++num;
		}
		for (int i = k + 1; i <= n; ++i)if(mm >= a[c[i]].x) {
			mm -= a[c[i]].x;++num;
		}
		gmax(NUM, num);
	}
	while (next_permutation(c + 1, c + n + 1));
}

int main()
{
	scanf("%d", &casenum);
	for (int casei = 1; casei <= casenum; ++casei)
	{
		scanf("%d%d%lld", &n, &k, &m);
		for (int i = 1; i <= n; ++i)
		{
			scanf("%d%d", &a[i].x, &a[i].y);
			a[i].d = a[i].x - a[i].y;
			a[i].o = i;
		}
		sort(a + 1, a + n + 1);
		int num = 0;
		sot.clear();
		for (int i = 1; i <= k; ++i)
		{
			if (m >= a[i].y)
			{
				m -= a[i].y;
				++num;
				sot.insert(a[i].d);
			}
			else break;
		}
		for (int i = k + 1; i <= n; ++i)b[i] = a[i];
		sort(b + k + 1, b + n + 1, cmp);
		MS(buy, 0);

		int p1 = k + 1;
		int p2 = k + 1;
		while (p1 <= n && p2 <= n)
		{
			if (buy[a[p1].o]) { ++p1; continue; }
			if (buy[b[p2].o]) { ++p2; continue; }

			int pri1 = sot.empty()?2e9:*sot.begin() + a[p1].y;
			int pri2 = b[p2].x;
			int minp = min(pri1, pri2);
			if (m < minp)break;
			m -= minp; ++num;
			if (pri1 < pri2)  {
				sot.erase(sot.begin());
				sot.insert(a[p1].d);
				buy[a[p1].o] = 1;
				++p1;
			}
			else {
				buy[b[p2].o] = 1;
				++p2;
			}
		}
		printf("%d\n", num);
	}
	return 0;
}
/*
【trick&&吐槽】
1,符号变量的对应不要搞错
2,set中删除元素要erase(迭代器)或者erase(set.find(val))
3,写讨论题或细节多的题的时候一定要稳住心态。越慌越错越慢。
4,我第一次按照a[i].x升序排序的时候,把a[i].d作为降序的第二关键字。这实际上是并不必要的。

【题意】
我们一开始有m元钱(0<=m<=1e14),手中有k(0<=k<=n)张万能折扣券。
有n(1e5)个蛋糕,每个蛋糕的初始价格是a[i].x,
如果我们对这个蛋糕使用一张万能折扣券,那么只要花a[i].y元就能买这个蛋糕。

问你,我们最多能买多少个蛋糕

【类型】
贪心 STL-SET

【分析】
这题用三步贪心就可以AC了。

第一步贪心:
我们首先要把所有蛋糕按照折扣价从小到大排序,然后看折扣后价格最小的k个蛋糕。
如果这k个蛋糕都买不下,答案已经出来了,从小开始,能买几个是几个。

第二步贪心:
现在我们至少可以买下折扣后的价格最便宜的k个了。
我们发现,在这个基础上,我们是不可能舍弃任何这前k个蛋糕中的任何一个的。

可以用反证法轻易证明:
假设我们丢掉了其中一个蛋糕,用这张折扣券买了任意一个其他蛋糕,
那么我们回过来,不丢掉这个蛋糕,价格一定是更便宜的,更优的。

第三步贪心
那这样,如果我们再想要买一个新的蛋糕,要怎么办呢?
简单地,我们只有两种决策:
1,不在新蛋糕上用折扣券,肯定选择非折扣价最小的蛋糕买。
最小成本是min(a[未选择蛋糕].x)
2,在新蛋糕上用折扣券,那么我们必然要舍弃以前一个蛋糕的折扣价。
最小成本是min(损失折扣差价)+a[未选择蛋糕].y
两者谁小,我们就选择谁。

最小损失折扣价用set维护
这样贪心到最后,这道题就可以AC啦。

【时间复杂度&&优化】
O(nlogn)

【数据】
2 1 6 3 2 5 3

*/



版权声明:本文为snowy_smile原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。