文章目录
模拟二叉堆
二叉堆是一种支持删除、插入、查询最值的数据结构,是满足堆性质的一颗完全二叉树,C++的priority_queue支持这些操作,但是不支持remove操作【以大根堆为例,注意这儿的n是堆中的元素个数,此n非彼n】
int n, head[N]; //层次序列存储方式
inline void up(int p) //p是下标
{ //向上调整【大根堆为例】
while (p > 1)
{
if (head[p] > head[p / 2])//小根堆则是head[p] < head[p / 2]
{
swap(head[p], head[p / 2]);
p /= 2;
}
else
break;
}
}
inline void d_insert(int val)
{ //插入数值val
head[++n] = val;
up(n); //调节的参数是下标
}
inline int get_top() { return head[1]; } //取堆顶元素
inline void down(int p)
{ //向下调整【大根堆为例】
int s = p * 2;
while (s <= n)
{
if (s < n && head[s] < head[s + 1]) //左右子节点取较大者,小根堆则是s < n && head[s] > head[s + 1]
s++;
if (head[s] > head[p])//小根堆则是head[s] < head[p]
{
swap(head[s], head[p]);
p = s, s = p * 2;
}
else
break;
}
}
inline void d_extract() //删除堆顶
{
head[1] = head[n--];
down(1);
}
inline void d_remove(int p) //删除下标p位置的结点
{
head[p] = head[n--];
up(p), down(p);
}
哈夫曼树【Huffman树】
有n个叶子结点的k叉树,i个叶子结点有权值wi,求最小化wi*Li,Li是第i个叶子结点到根节点的距离,该问题被称为k叉哈夫曼树,每个结点都有k-1个叶子节点【注意当k>2时,应该要补充一些额外的0权值的叶子结点,使得满足(n-1)%(k-1)==0,保证了贪心策略的正确性】
牛客一个小栗子荷马史诗
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef unsigned long long ull;
typedef pair<long long, long long> pll;
const int N = 1e5 + 10;
const int M = 3e6 + 10;
const int mod = 2147483647;
const int inf = 0x3f3f3f3f;
const int eps = 1e-6;
#define IOS \
ios::sync_with_stdio(0); \
cin.tie(0); \
cout.tie(0)
ll n, k, ans, sum, dep;
pll a[N]; //first表示出现次数,second表示该结点在树中的深度
priority_queue<pll, vector<pll>, greater<pll>> p;
int main()
{
IOS;
cin >> n >> k;
for (ll i = 1; i <= n; i++)
{
cin >> a[i].first;
p.push({a[i].first, 0});
}
while ((n - 1) % (k - 1)) //额外加一些权值为0的叶子结点,保证贪心策略的正确性
{
p.push({0, 0});
n++; //加上点之后总结点数要++
}
while (p.size() > 1)
{
sum = 0, dep = 0;
for (int i = 0; i < k; i++) //k叉哈夫曼树,每个结点有k-1个是叶子节点
{ //k要多次循环利用,不能写成while(k--)
pll x = p.top();
p.pop();
sum += x.first;
dep = max(dep, x.second);
}
ans += sum; //每次有结点会重复累加,所以这样的ans其实就是每个叶子节点的[权值*深度]的和
p.push({sum, dep + 1}); //每次推入堆中的是合并后的值,并非总的值
}
cout << ans << "\n"
<< p.top().second << endl;
return 0;
}