题目链接

题目大意:构造\(k\)叉哈夫曼树

哈夫曼树


分析:这题比较模板,可以当做复习初赛(大雾)

哈夫曼树的定义:

构造一棵有\(n\)个节点的\(k\)叉树,每个节点带有权\(w_i\),深度\(d_i\)(规定根深度为\(0\)),使得\(\sum w_i \times d_i\)最小

\(k=2\)时即为合并果子,因此可以想出显而易见的贪心算法,每次从堆中取\(k\)个合并,但是这样可能根节点的儿子不足\(k\)个,此时将叶节点移为根节点儿子会使得答案更优

因此我们需要额外补足一些\(w=0\)的虚拟点,让儿子不足的情况发生在叶子结点

每次将\(k\)个节点合并使得总个数减少\(k-1\)个,使得\(n\)个节点合并为\(1\)个减少了\(n-1\)个,因此当\((k-1) | (n-1)\)时上述贪心算法正确

题目还要求代价和最小时最大深度尽量小,权值相同时按照深度排序即可

#include <cstdio>
#include <cctype>
#include <queue>
using namespace std;
typedef long long ll;
inline ll read(){
	ll x = 0;char c = getchar();
	while(!isdigit(c))c = getchar();
	while(isdigit(c))x = x * 10 + c - '0',c = getchar();
	return x;
}
struct HeapNode{
	ll val;
	int dep;
	bool operator < (const HeapNode &rhs)const{
		return (val == rhs.val) ? (dep > rhs.dep) : val > rhs.val;
	}
};
priority_queue<HeapNode> Q;
int n,k;
ll ans1,ans2;
int main(){
	n = read(),k = read();
	for(int i = 1;i <= n;i++)
		Q.push(HeapNode{read(),0});
	while((n - 1) % (k - 1))Q.push(HeapNode{0,0}),n++;
	while(Q.size() != 1){
		ll add1 = 0,add2 = 0;
		for(int i = 1;i <= k;i++){
			HeapNode h = Q.top();Q.pop();
			add1 += h.val;
			add2 = max(add2,(ll)h.dep);
		}
		ans1 += add1;
		ans2 = max(ans2,add2 + 1);
		Q.push(HeapNode{add1,add2 + 1});
	}
	printf("%lld\n%lld\n",ans1,ans2);
	return 0;
}

相关文章: