1. 程式人生 > >2018.10.01【校內模擬】購買書籍(貪心)(堆)(set實現堆)

2018.10.01【校內模擬】購買書籍(貪心)(堆)(set實現堆)

描述

L的書籍被M偷了以後傷心欲絕,決定再購買一些回來,現在有 N 本書可以買,每本書的價格是 a[i]元。

現在L總共有 M 元,以及 K 張優惠券。 對於每本書,如果使用一張優惠券,則可以用b[i]的優惠價格購買。 注意每本書只能使用一張優惠券,只能購買一次。

L想知道自己最多可以購買幾本書?

輸入

第一行三個整數 N, K, M

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

輸出

一個整數表示答案。

樣例輸入

一個整數表示答案。

樣例輸出

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

解析:

場上寫了70分暴力結果因為讀入優化寫掛而光榮爆0的蒟蒻。現在來發一個沒人看的題解。

思路:

這道題貪心很好想啊,中等資料動態規劃也很好想啊。 中等資料就是做一個揹包問題就好了啊。

而大資料隨機化貪心的蒟蒻就直接涼涼了。

因為資料範圍實在是很大,貪心幾次就ggggT了。

然而這道題有淺顯易懂的一句話題解。。。

先使用 K 張優惠券,買價格最小的。 如果考慮如何擴充套件當前解 1、 直接購買一個 a[i] 2、退掉一個用優惠券的,用優惠券去買別的 b[j]+(a[i]-b[i]) 2 種情況看哪一個更優 用堆維護 a[i], b[i], a[i]-b[i]

好吧這是六句

而擴充套件到不能夠再次擴充套件的情況下就是最優了,因為我們從構造方案來說,每次的選取都是向著 儘可能多拿,少花錢 的原則做的。

其實可以用網路流退流操作理解一下。但是如果真的寫網路流光建圖就gg了。

注意實現的時候有點問題。 我們選擇了一個物品以優惠價或全價購買,它在另一個集合裡面就要刪除。 而如果真的寫堆的話就要手寫decreasekeydecrease-key,每次刪除就是decreasekeydecrease-key當前節點到INF-INF,然後poppop

寫起來很麻煩,所以就直接上一個setset

et模擬堆就行了。

程式碼:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define pc putchar
#define cs const

inline char get_char(){
	static const int Rlen=1<<18;
	static char buf[Rlen],*p1=buf,*p2=buf;
	return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++; 
}

inline
ll getint(){
	re ll num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return num;
}

cs int N=100006;
struct node{
	int a,b;
}p[N];

inline bool cmp1(cs node &a,cs node &b){
	return a.a<b.a;
}

inline bool cmp3(cs node &a,cs node &b){
	return a.b<b.b;
}

set<int> s1;
set<pair<int,int> > s2,s3;
int n,k;
ll m;
int ans;

signed main(){
	n=getint();
	k=getint();
	m=getint();
	for(int re i=1;i<=n;++i)p[i].a=getint(),p[i].b=getint();
	if(k==0){
		sort(p+1,p+n+1,cmp1);
		for(int re i=1;i<=n;++i){
			if(m>=p[i].a)m-=p[i].a,++ans;
			else break;
		}
		cout<<ans<<endl;
		return 0;
	}
	sort(p+1,p+n+1,cmp3);
	for(int re i=1;i<=min(n,k);++i){
		if(m>=p[i].b)m-=p[i].b,++ans,s1.insert(p[i].a-p[i].b);
		else return cout<<ans<<endl,0;
	}
	s2.clear();
	s3.clear();
	for(int re i=k+1;i<=n;++i){
		s2.insert(make_pair(p[i].b,i));
		s3.insert(make_pair(p[i].a,i));
	}
	while(!s2.empty()){
		ll f1=*s1.begin()+s2.begin()->first,f2=s3.begin()->first;
		if(f1<=f2){
			if(m>=f1)m-=f1,++ans;
			else break;
			int id=s2.begin()->second;
			s3.erase(make_pair(p[id].a,id));
			s1.erase(s1.begin());
			s2.erase(s2.begin());
			s1.insert(p[id].a-p[id].b);
		}
		else {
			if(m>=f2)m-=f2,++ans;
			else break;
			int id=s3.begin()->second;
			s3.erase(s3.begin());
			s2.erase(make_pair(p[id].b,id));
		}
	}
	cout<<ans<<endl;
	return 0;
}