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
见祖宗!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具