【貪心】購買書籍

題面

L的書籍被M偷了以後傷心欲絕,決定再購買一些回來,現在有 N 本書可以買,每本書的價格是 a[i]元。
現在L總共有 M 元,以及 K 張優惠券。 對於每本書,如果使用一張優惠券,則可以用b[i]的優惠價格購買。 注意每本書只能使用一張優惠券,只能購買一次。
L想知道自己最多可以購買幾本書?

輸入

第一行三個整數 N, K, M
接下來 N 行,每行兩個整數,表示 a[i]和 b [i]。

輸出

一個整數表示答案。

樣例輸入

4 1 7
3 2
2 2
8 1
4 3

樣例輸出

3

提示
【解釋】
選擇第 1、 2、 3 本書,其中第3本使用優惠券。總共 5 元。
【數據規模】
對於 20%:N<=10
對於 50%:N<=100
對於另外 20%:K = 0
對於 100%:1<=N<=100000,0<=K<=N,M<=1014,1<=b[i]<=a[i]<=109
標籤

解答

感謝熊姐姐的題解Orz,太聚了。貪心題,不算特別難想,但是我也想不到。。。。於是隨機化貪心過了。。。。。。。。。zxy的hack數據也沒hack掉(蒟蒻光環)
先說隨機化貪心的方法,先按照優惠價排序,按照優惠價購買,買完以後如果有剩餘的錢就按照原價最小買Orz。然後由於我感覺不太穩,把原序列random_shuffle一下,再多跑幾遍,跑的時候2種策略計算答案,就A了。你來卡我啊
貼隨機化貪心代碼:前面還有一通亂搞亂排序

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <ctime>
using namespace std;
const int MAXN = 100010;
long long n,m,k;
struct book{
	long long a,b;
}bs[MAXN];
bool cmp1(book &b1,book &b2){
	return b1.a<b2.a;
}
bool cmp2(book &b1,book &b2){
	return b1.b<b2.b;
}
bool cmp3(book &b1,book &b2){
	return b1.a-b1.b<b2.a-b2.b;
}
bool cmp4(book &b1,book &b2){
	return b1.a*b1.b<b2.a*b2.b;
}
bool cmp5(book &b1,book &b2){
	return b1.a/b1.b<b2.a/b2.b;
}
long long work(){
	long long sum = m,tot = 0,used = 0;
	for(int i = 1;i<=n;i++){
		if(sum>=bs[i].a)tot++,sum-=bs[i].a;
		else if(used<k&&sum>=bs[i].b)used++,sum-=bs[i].b,tot++;
	}
	long long ret = tot;
	sum = m,tot = 0,used = 0;
	for(int i = 1;i<=n;i++){
		if(sum>=bs[i].b&&used<k)tot++,sum-=bs[i].b,used++;
		else if(sum>=bs[i].a)used++,sum-=bs[i].a,tot++;
	}
	return max(tot,ret);
}
int main(){
	#ifdef BEYONDSTARS
		freopen("testdata.in","r",stdin);
	#endif
	srand(time(0));
	scanf("%lld %lld %lld",&n,&k,&m);
	for(int i = 1;i<=n;i++){
		scanf("%lld %lld",&bs[i].a,&bs[i].b);
	}
	long long ans = work();
	sort(bs+1,bs+1+n,cmp1);ans = max(ans,work());
	sort(bs+1,bs+1+n,cmp2);ans = max(ans,work());
	sort(bs+1,bs+1+n,cmp3);ans = max(ans,work());
	sort(bs+1,bs+1+n,cmp4);ans = max(ans,work());
	sort(bs+1,bs+1+n,cmp5);ans = max(ans,work());

	if(n>5000){
		for(int i = 1;i<=200;i++){
			random_shuffle(bs+1,bs+1+n);ans = max(ans,work());
		}
	}else{
		for(int i = 1;i<=5000;i++){
			random_shuffle(bs+1,bs+1+n);ans = max(ans,work());
		}
	}
	printf("%lld",ans);
	return 0;
}

正解:還是貪心,就是優先購買優惠價低的,然後對於剩下的東西,從原價最小開始枚舉,看看是把之前用了券的退掉,這個東西用優惠價便宜還是直接買便宜。於是3個set維護差值,優惠價,原價就好了。Orz wtcl

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <set>
using namespace std;
typedef pair<int,int> pii;
const int MAXN = 100010;
long long a[MAXN],b[MAXN],id[MAXN];
set<pii> s1,s2,s3;
bool cmp(int x,int y){return b[x]<b[y];}
int main(){
	int n,k;long long m;scanf("%d %d %lld",&n,&k,&m);
	for(int i = 1;i<=n;i++)scanf("%lld %lld",&a[i],&b[i]),id[i] = i;
	sort(id+1,id+1+n,cmp);
	int ans = 0;
	if(k==0){
		sort(a+1,a+1+n);
		for(int i = 1;i<=n;i++){
			if(m>a[i])ans++,m-=a[i];
			else break;
		}
		printf("%d",ans);return 0;
	}
	long long now = 0;
	for(int i = 1;i<=k;i++){
		now+=b[id[i]];if(now>m){printf("%d",ans);return 0;}
		ans++;
		s1.insert(make_pair(a[id[i]]-b[id[i]],id[i]));
	}
	for(int i = k+1;i<=n;i++){
		s2.insert(make_pair(b[id[i]],id[i]));
		s3.insert(make_pair(a[id[i]],id[i]));
	}
	while(s2.size()){
		long long f1 = s1.begin()->first+s2.begin()->first,f2 = s3.begin()->first;
		if(f1<f2){
			now+=f1;
			if(now>m)return 0*printf("%d",ans);
			ans++;
			int id_ = s2.begin()->second;
			s1.erase(s1.begin());s2.erase(s2.begin());
			s3.erase(s3.find(make_pair(a[id_],id_)));
			s1.insert(make_pair(a[id_]-b[id_],id_));
		}else{
			now+=f2;if(now>m)return 0*printf("%d",ans);
			ans++;
			int id_ = s3.begin()->second;
			s2.erase(s2.find(make_pair(b[id_],id_)));s3.erase(s3.begin());
		}
	}
	printf("%d",ans);
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章