codeforces335F Buy One, Get One Free

題面

題意

你要買n個東西,當你買了一個價格爲x的東西時,可以讓商店免費贈送你一個價格嚴格小於x的東西,則你買下這n個東西至少要花多少錢。

做法

n很大,考慮貪心。
首先根據商品的代價從大到小排序,價格相同的商品一起考慮。
可以把所有贈送的商品的價格都放到一個小根堆裏,當我們考慮價格爲x的商品時,記一共有sum個商品的價格比x大,首先將可以送的商品直接贈送(共有min(sum2pq.size(),cnt[x])\min(sum-2*pq.size(),cnt[x])個),然後考慮剩下的價格爲x的商品。
若此時堆首的商品價格大於x,令堆首的商品價格爲y,則若買下y,可以多贈送兩個x。
如果2x<y2*x<y,顯然不考慮買下y。
反之,則考慮買下y,這樣原來是買下某件商品a,送了y,並買下了2個x,變成了買下了a和y送了2個x,後者雖然更省錢,但少送了兩個更廉價的商品,因此可以把後者看作是多送了一個價格爲2xy2*x-y的商品,將2xy2*x-y也加入堆中。
若此時堆首的商品價格小於x,則替換,改爲買那件商品並贈送至多兩個x。
最後堆中的元素和即爲剩下的錢。

代碼

#include<bits/stdc++.h>
#define ll long long
#define N 500100
using namespace std;

ll n,sum,bb,top,ans,num[N],cnt[N],b[N],sta[N];
priority_queue<ll,vector<ll>,greater<ll> >pq;

inline bool cmp(ll u,ll v){return u>v;}
int main()
{
    ll i,j,t,mx;
    cin>>n;
    for(i=1;i<=n;i++) scanf("%lld",&num[i]),ans+=num[i];
    sort(num+1,num+n+1,cmp);
    for(i=1;i<=n;i++)
    {
	if(i==1 || num[i]!=num[i-1]) b[++bb]=num[i];
	cnt[bb]++;
    }
    for(i=1;i<=bb;i++)
    {
	top=0;
	mx=min(sum-(ll)pq.size()*2,cnt[i]);
	for(j=1;j<=mx;j++) sta[++top]=b[i];
	mx=min(sum,cnt[i])-mx;
	for(j=1;j<=mx;j+=2)
	{
	    t=pq.top();pq.pop();
	    if(t<b[i])
	    {
		sta[++top]=b[i];
		if(j<mx) sta[++top]=b[i];
	    }
	    else
	    {
		sta[++top]=t;
		if(j<mx && 2*b[i]>t) sta[++top]=2*b[i]-t;
	    }
	}
	for(j=1;j<=top;j++) pq.push(sta[j]);
	sum+=cnt[i];
    }
    for(;!pq.empty();pq.pop()) ans-=pq.top();
    cout<<ans;
}

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