P1090 合并果子 题解复盘
模块:贪心算法
目标:每次合并当前最小的两堆果子,使总代价最小
基本信息
| 项目 | 内容 |
|---|---|
| 题目编号、来源 | P1090 洛谷 / 合并果子 |
| 训练层级 | A 模板题 |
| 知识版块 | 贪心、优先队列、小根堆 |
解题前 · 关键信号识别
| 维度 | 分析 |
|---|---|
| 目标、约束、底层结构 | 目标 :将 n 堆果子合并成一堆,使所有合并代价之和最小。 约束 :每次只能合并两堆果子,合并代价为两堆果子的重量之和。 底层结构:每次选择当前最优选择,属于优先队列贪心问题。 |
| 数据规模 | n≤10000,如果每次排序寻找最小两个会增加复杂度,使用小根堆可以优化。 |
| 候选算法和依据 | 算法:贪心 + 小根堆。 依据:每次选择当前最小的两堆果子进行合并,可以使较小的代价尽量少参与后续合并,从而降低总代价。 |
| 复杂度预判 | 建堆 O(n),每次取出和加入操作 O(logn),共 n-1 次。 总时间复杂度 O(nlogn),空间复杂度 O(n)。 |
解题后 · 外化复盘
| 维度 | 内容 |
|---|---|
| 实现结构 / 核心思路 | 使用小根堆 priority_queue<int,vector<int>,greater<int>> 保存所有果子重量。每次取出堆顶的两个最小值 a、b,合并代价加入答案 ans+=a+b,然后将新的果子重量 a+b 放回堆中。直到堆中只剩一堆果子。 |
| 错因回溯 | 1. 容易想到排序后取两个最小值,但是每次合并后还需要重新寻找最小值,效率较低。2. 合并代价可能较大,需要使用 long long 保存答案。❤️. 容易忘记将合并后的新果子重新加入堆中。 |
| 边界和易错点 | 1. 循环条件为 q.size()>1,只剩一堆时结束。2. 使用小根堆,不是默认大根堆。3. 每次合并后的新重量必须重新 push。4. 答案使用 long long。 |
| 下次看到什么信号,我应该想到这个方法 | 看到:① 合并多个元素② 每次合并产生代价③ 要求总代价最小想到:优先队列贪心,每次取最小的两个。 |
AC 完整代码
cpp
#include <iostream>
#include <queue>
using namespace std;
int main()
{
int n;
cin>>n;
priority_queue<int,vector<int>,greater<int>> q;
for(int i=0;i<n;i++)
{
int x;
cin>>x;
q.push(x);
}
long long ans=0;
while(q.size()>1)
{
int a=q.top();
q.pop();
int b=q.top();
q.pop();
ans+=a+b;
q.push(a+b);
}
cout<<ans;
return 0;
}
P2676 USACO07DEC Bookshelf B 题解复盘
模块:贪心算法
目标:在奶牛总高度达到书架高度的前提下,选择最少数量的奶牛
基本信息
| 项目 | 内容 |
|---|---|
| 题目编号、来源 | P2676 洛谷 / USACO07DEC Bookshelf B |
| 训练层级 | A 模板题 |
| 知识版块 | 贪心、排序、选择问题 |
解题前 · 关键信号识别
| 维度 | 分析 |
|---|---|
| 目标、约束、底层结构 | 目标 :选择尽可能少的奶牛,使它们的身高总和 ≥ 书架高度 B。 约束 :每头奶牛只能选择一次,选择数量越少越稳定。 底层结构:在满足总高度要求的情况下,让贡献大的元素优先加入,可以更快达到目标。 |
| 数据规模 | N≤20000,奶牛数量较多,需要 O(nlogn) 或 O(n) 级别算法。 |
| 候选算法和依据 | 算法:贪心 + 排序。 依据:为了使用最少数量的奶牛,应该优先选择身高最高的奶牛,这样可以用更少的数量达到目标高度。 |
| 复杂度预判 | 排序 O(nlogn),遍历 O(n)。 总时间复杂度 O(nlogn),空间复杂度 O(n)。 |
解题后 · 外化复盘
| 维度 | 内容 |
|---|---|
| 实现结构 / 核心思路 | 1. 使用数组保存所有奶牛身高。 2. 按身高从大到小排序。 3. 从最高的奶牛开始累加高度,每选择一头奶牛,答案加一。 4. 当累计高度 sum>=B 时停止,此时选择的奶牛数量就是最少数量。 |
| 错因回溯 | 1. 容易想到从小到大选择,但这样会需要更多奶牛。 2. 高度总和可能超过 int 范围,因此累计高度使用 long long。 3. 排序方向错误会导致无法保证数量最少,需要降序排序。 |
| 边界和易错点 | 1. 一头奶牛高度可能已经达到 B,答案为 1。 2. 累加高度使用 long long。 3. 排序使用降序:greater<int>()。 4. 达到目标后及时退出循环。 |
| 下次看到什么信号,我应该想到这个方法 | 看到:① 最少选择多少个元素② 每个元素贡献不同③ 达到某个目标值想到:排序贪心,优先选择贡献最大的元素。 |
AC 完整代码
cpp
#include <iostream>
#include <vector>
#include <queue>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <iomanip>
using namespace std;
int main(){
int n,b;
cin>>n>>b;
vector<int> v;
for(int i=0;i<n;i++){
int a;
cin>>a;
v.push_back(a);
}
sort(v.begin(),v.end(),greater<int>());
int ans=0;
long long s=0;
for(int i=0;i<n;i++){
s+=v[i];
ans++;
if(s>=b) break;
}
cout<<ans;
return 0;
}