题意分析
- 给你一个序列,需要将这个序列进行两两合并,最后合并成一个数字。每次合并的花费就是这两个数字的和乘上一个c,问合并成一个数字的最小的花费是多少?
思路分析
- 学过数据结构的同学应该都知道,这一题是一个很经典的类似于哈夫曼树的题目。解题的方法就是每次找到当前最小的两个数字,然后将这两个数字合并成一个数字放到原来的集合里面。每次累加花费即可。所以其实就是要我们构造出一棵哈夫曼树,在每次合并子节点的过程中累加我们得到的结果即可。
解法一 优先队列(小根堆)
在C++里面有一个很好用的STL容器,就是小根堆了,它为我们封装好了堆的一些基本的操作。每次都会自动调整这个堆,使得最小值在堆顶,我们可以直接O(1)取堆顶的数字即可。
代码如下
- 需要遍历n个数字,每次堆都会进行一次调整。调整的时间复杂度为
,所以总的时间复杂度为
- 我们利用小根堆存储了序列。空间复杂度为
- 需要遍历n个数字,每次堆都会进行一次调整。调整的时间复杂度为
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param n int整型 表示有n个数。
* @param c int整型 参数c
* @param a int整型vector ai表示第i个数的大小
* @return long长整型
*/
long long solve(int n, int c, vector<int>& a) {
// write code here
// 定义一个小根堆
priority_queue<long long,vector<long long>,greater<long long> > q;
for(auto x:a){
q.push(x);
}
long long ans=0;
while(1){
// 如果此时堆中的元素的个数小于2,那么得到了最后的结果
if(q.size()==1) break;
// 取出堆顶的两个最小的数字
long long tmp1=q.top();
q.pop();
long long tmp2=q.top();
q.pop();
// 组合成一个新的数字
ans+=c*(tmp1+tmp2);
// 新组成的数字入堆
q.push(tmp1+tmp2);
}
return ans;
}
};解法二 (map红黑树)
- 我们平时经常用map来进行离散化,但是可能很少有人注意到我们map内部其实是可以进行排序的,它的内部是红黑树实现。按照键的大小进行排序的。所以我们可以利用这个性质解决本题。我们先将数字都存入map里面。然后我们可以每次取出map指针第一个位置的元素,取出这个元素。如果这个元素取完了,那么在map里面删除这个元素即可。
- 代码如下
- 每次map里面的元素变动都会导致map进行重新调整,时间复杂度为
- 利用map存储元素的值,空间复杂度为
- 每次map里面的元素变动都会导致map进行重新调整,时间复杂度为
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param n int整型 表示有n个数。
* @param c int整型 参数c
* @param a int整型vector ai表示第i个数的大小
* @return long长整型
*/
typedef long long ll;
ll get(map<ll,ll> &mp){
auto x=*mp.begin();
int y=x.first;
mp[y]--; //取出一个
if(mp[y]==0){ // 如果此时的数字取完了,那么删除这个数字
mp.erase(mp.find(y));
}
return y;
}
long long solve(int n, int c, vector<int>& a) {
// write code here
map<ll,ll> mp;
for(auto x:a){
mp[x]++;
}
int cnt=0; // 记录消除了多少个数字
ll ans=0;
while(cnt<n-1){
int x=get(mp); // 获得map里面的键最小的数字
int y=get(mp); // 获得map里面的键最小的数字
ans+=c*(x+y);
mp[(x+y)]++;
cnt++;
}
return ans;
}
};
京公网安备 11010502036488号