P7891 [入门赛 #10] Coin Selection G(hard version) 题解

题目传送门

错误思路

暴力模拟,显然 $O(n^2)$,过不了 $1 \leq n \leq 10^5$。

正解

思路

重新看题面,可以发现想快速查找

面值不超过当前自己钱包中硬币的总面额的硬币中面额最大的一枚硬币

可使用二分法。

算法流程

  • 将读入的硬币数组排序(或在输入时就用二分插入使数组有序)。
  • 判断当前自己钱包里已有硬币的总面额是否大于等于最小的硬币。
    • 若是,则取合适的硬币。
    • 若不是,则取面值最小的硬币。
  • Farmer John 和 Bessie 交替取硬币。

STL

因为硬币一直被取走,所以可以用 vector 动态存储硬币面值。

lower_bound(begin,end,value) 查找从小到大排序的数组在[begin,end)区间中第一个大于等于 value 的数,并返回一个迭代器指向此数。

代码

//by Po7ed
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
long long n;
scanf("%lld",&n);
vector<long long> v;//存储硬币面值
long long t;
for(long long i=0;i<n;i++)
{
scanf("%lld",&t);
v.insert(lower_bound(v.begin(),v.end(),t,greater<long long>()),t);
//此处在输入时直接用lower_bound插入面值。使得v有序(从大到小)。
//lower_bound用greater<long long>()重载,使lower_bound知道v是从大到小排序的。下同。
}
long long a=0,b=0;
for(long long i=1;v.size();i++)
{
if(i&1)//当取奇数次时
{
if(a<v.back())//如果面值不够
{
a+=v.back();
v.pop_back();
}
else//如果面值够
{
t=lower_bound(v.begin(),v.end(),a,greater<long long>())-v.begin();
//查找合适的硬币
a+=v[t];
v.erase(v.begin()+t);
}
}
else//当取偶数次时
{
if(b<v.back())//如果面值不够
{
b+=v.back();
v.pop_back();
}
else//如果面值够
{
t=lower_bound(v.begin(),v.end(),b,greater<long long>())-v.begin();
//查找合适的硬币
b+=v[t];
v.erase(v.begin()+t);
}
}
}
printf("%lld %lld",a,b);
return 0;
}

注意事项

不开 long long 见祖宗!

参考

upper_bound和lower_bound用法(史上最全)

posted @   Po7ed  阅读(29)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示