[关闭]
@xiaoziyao 2021-01-29T11:44:15.000000Z 字数 1978 阅读 815

CF335F Buy One, Get One Free解题报告

解题报告


CF335F Buy One, Get One Free解题报告:

更好的阅读体验

题意

分析

反悔贪心。

我们考虑选择若干个馅饼使得它们免费,答案为所有馅饼的总价减去免费的馅饼的价格之和。

首先把价格相同的馅饼合并,然后把馅饼按价格从大到小排序,那么现在每一次赠送都只能依赖前面买过的馅饼。

我们设之前有个馅饼是免费得到的,之前总共得到了个馅饼,现在的馅饼价格为,有个,那么就有个馅饼是全价买的。

我们考虑先贪心地把前面全部的全价购买的馅饼兑换完,也就是先得到个馅饼,那么还剩个馅饼。

我们考虑把之前的所有兑换操作都保存在一个数据结构里,那么我们可以对于里面的某些馅饼进行反悔,并重新选择当前的馅饼。

我们考虑之前免费得到的某个价格为的馅饼,我们对的大小讨论(你可能想问不是很显然吗,但是由于在后文中我们加入的馅饼价格会有变化,因此并不满足,这里需要讨论):

你可能直接用了个数组保存可以反悔的决策,然而你会在挂掉:

  1. 7
  2. 10 9 5 5 4 3 2

你选取了,然后兑换,然后遇到两个,抽象出一个价格为的馅饼加入,然后遇到,能反悔的第一个决策价格为因此没有反悔但因为放到了后面,,遇到直接兑换,而遇到而当前反悔的决策为也可以直接兑换。因此所有反悔的馅饼只包含,花费为

实际上我们选取花费为更少,这告诉我们每次反悔的时候需要优先选取价值更小的决策,我们可以选取一个优先队列来保存决策就可以AC。

时间复杂度:我们考虑每次枚举堆内的馅饼都会把某个决策掉,而在中每个馅饼也只能提供一次入堆的机会,因此复杂度为

代码

注意,这里的小根堆用权值取负实现。

  1. #include<stdio.h>
  2. #include<algorithm>
  3. #include<queue>
  4. #define int long long
  5. using namespace std;
  6. const int maxn=500005;
  7. int n,ans,tot,sum;
  8. int a[maxn],s[maxn],c[maxn],tmp[maxn];
  9. priority_queue<int>q;
  10. inline int cmp(int a,int b){
  11. return a>b;
  12. }
  13. signed main(){
  14. scanf("%lld",&n);
  15. for(int i=1;i<=n;i++)
  16. scanf("%lld",&a[i]),ans+=a[i];
  17. sort(a+1,a+1+n,cmp);
  18. for(int i=1;i<=n;i++){
  19. if(i==1||a[i]!=a[i-1])
  20. s[++tot]=a[i],c[tot]++;
  21. else c[tot]++;
  22. }
  23. for(int i=1;i<=tot;i++){
  24. int ok=min(sum-(int)q.size()*2,c[i]),tmps=0,rst=min(c[i]-ok,sum-ok);
  25. for(int j=1;j<=ok;j++)
  26. tmp[++tmps]=s[i];
  27. for(int j=1;j<=rst;j+=2){
  28. int k=-q.top();
  29. q.pop();
  30. if(k<s[i]){
  31. tmp[++tmps]=s[i];
  32. if(j<rst)
  33. tmp[++tmps]=s[i];
  34. }
  35. else{
  36. tmp[++tmps]=k;
  37. if(j<rst)
  38. tmp[++tmps]=2*s[i]-k;
  39. }
  40. }
  41. for(int j=1;j<=tmps;j++)
  42. if(tmp[j]>=0)
  43. q.push(-tmp[j]);
  44. sum+=c[i];
  45. }
  46. while(!q.empty())
  47. ans+=q.top(),q.pop();
  48. printf("%lld\n",ans);
  49. return 0;
  50. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注