題面
題意
你要買n個東西,當你買了一個價格爲x的東西時,可以讓商店免費贈送你一個價格嚴格小於x的東西,則你買下這n個東西至少要花多少錢。
做法
n很大,考慮貪心。
首先根據商品的代價從大到小排序,價格相同的商品一起考慮。
可以把所有贈送的商品的價格都放到一個小根堆裏,當我們考慮價格爲x的商品時,記一共有sum個商品的價格比x大,首先將可以送的商品直接贈送(共有個),然後考慮剩下的價格爲x的商品。
若此時堆首的商品價格大於x,令堆首的商品價格爲y,則若買下y,可以多贈送兩個x。
如果,顯然不考慮買下y。
反之,則考慮買下y,這樣原來是買下某件商品a,送了y,並買下了2個x,變成了買下了a和y送了2個x,後者雖然更省錢,但少送了兩個更廉價的商品,因此可以把後者看作是多送了一個價格爲的商品,將也加入堆中。
若此時堆首的商品價格小於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;
}