K叉哈夫曼树的做法与二叉哈夫曼树相类似,只不过每次弹出的数为K个。
最关键的问题在于,当k大于2时,按照这个步骤做下去可能到最后剩下的元素少于k个,可以增加(k - 1) - (n - 1) % (k - 1)个权值为0的叶子节点作虚拟点。
为什么必须要保证最后剩下的元素恰好是k个呢?假如最后的元素少于k个,那么我们完全可以前面已经加入的数随便拉一个过来补上,此时这个点的权值不变但距离变小,自然最后的结果会更小。
还有要注意的一点是一定要先判断一下(n - 1) % (k - 1),然后再加虚拟节点,至于为什么应该不用我说了吧。
#include <cstdio>
#include <queue>
using namespace std;
struct SNode {
long long iData, iSum, iDepth;
};
bool operator > (const SNode a, const SNode b)
{
if (a.iData == b.iData)
return a.iDepth > b.iDepth;
return a.iData > b.iData;
}
int iWordType, iNumType;
priority_queue<SNode, vector<SNode>, greater<SNode> > theQueue;
int main()
{
scanf("%d %d", &iWordType, &iNumType);
for (int i = 1; i <= iWordType; i++)
{
SNode tmp;
scanf("%lld", &tmp.iData);
tmp.iSum = 0;
tmp.iDepth = 0;
theQueue.push(tmp);
}
if ((iWordType - iNumType) % (iNumType - 1))
for (int i = 1; i <= (iNumType - 1) - (iWordType - iNumType) % (iNumType - 1); i++)
{
SNode tmp;
tmp.iData = tmp.iSum = tmp.iDepth = 0;
theQueue.push(tmp);
}
while (theQueue.size() > 1)
{
SNode sum, tmp;
tmp.iData = tmp.iSum = tmp.iDepth = 0;
sum = tmp;
for (int i = 1; i <= iNumType; i++)
{
tmp = theQueue.top();
theQueue.pop();
sum.iData += tmp.iData;
sum.iSum += tmp.iData + tmp.iSum;
if (sum.iDepth <= 0 || sum.iDepth < tmp.iDepth + 1)
sum.iDepth = tmp.iDepth + 1;
}
if (sum.iData == 0)
sum.iDepth--;
theQueue.push(sum);
}
printf("%lld\n%lld", theQueue.top().iSum, theQueue.top().iDepth);
return 0;
}