148. 合并果子
题目: 148. 合并果子 - AcWing题库https://www.acwing.com/problem/content/150/
所有果子按照种类分成不同的堆, 比如三种果子分别是 1 2 9 个, 求合并的最小体力值
思路:
解题方法来自acwing: AcWing 148. 合并果子 - AcWinghttps://www.acwing.com/solution/content/3258/合并最小体力: 1 + 2 = 3; 3 + 9 = 12; 3 + 12 = 15
可以看成是一棵树, 一颗完全二叉树, 每个根节点都有左右两个儿子, 最终结果就是所有子节点相加, 拆分开看, 就是最底层节点的值*到根节点的路径长度, 所以值小的要放在深度深的位置
-
在所有点中, 最小的两个点一定深度最深, 且可以互为兄弟.
-
每次合并重量最小的两堆果子即可
时间复杂度
使用小根堆维护所有果子,每次弹出堆顶的两堆果子,并将其合并,合并之后将两堆重量之和再次插入小根堆中。
每次操作会将果子的堆数减一,一共操作 n−1次即可将所有果子合并成1堆。每次操作涉及到2次堆的删除操作和1次堆的插入操作,计算量是 O(logn)。因此总时间复杂度是 O(nlogn)。
代码
#include <iostream>
#include <algorithm>
#include <queue>
//贪心问题-huffman树 148.合并果子
using namespace std;
int main()
{
int n;//果子的种类数
scanf("%d", &n);//读取n
//用小跟堆维护所有果子,每次弹出堆顶的两堆果子,并将其合并,合并之后将两堆重量之和再次插入小根堆中。
priority_queue<int, vector<int>, greater<int>> heap;
while (n -- )//枚举果子种类 读取每种果子个数
{
int x;//每种果子个数
scanf("%d", &x);//读取个数
heap.push(x);//放入堆中
}
int res = 0;//耗费的最小体力, 结果
while (heap.size() > 1)//只要最后堆中是 > 1就继续合并
{
int a = heap.top(); heap.pop();//先取得第一个堆顶 a, 再把堆顶弹出
int b = heap.top(); heap.pop();//在取得新的堆顶 b, 再把堆顶弹出
res += a + b; //耗费的体力是a + b
heap.push(a + b); //再把新的果子数加起来 放回堆中
}
printf("%d\n", res);//输出结果
return 0;
}