題面
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;
}