1. 程式人生 > 其它 >[荷馬史詩] — k叉哈夫曼樹

[荷馬史詩] — k叉哈夫曼樹

題目背景

    追逐影子的人,自己就是影子 ——荷馬

題目描述

  

輸入格式

  

輸出格式

  

輸入輸出樣例

【說明/提示】

【資料規模與約定】

  


題意分析

  依據題意,就是要求構造一個K進位制的赫夫曼編碼。

  我們需要求的是樹的WPL和該赫夫曼樹的高度。所以在struct裡面不僅要記錄結點的w,還要記錄深度h,並以他們作為小根堆的關鍵字。

  對於k叉赫夫曼樹的求解,直觀的想法是在貪心的基礎上,改為每次從堆中去除最小的k個權值合併。我們便可以按照赫夫曼樹的構造方式,將當前最小的K個節點合併為1個父節點(顯然,可以使用優先佇列(二叉堆)進行維護),直至只有一個父節點。

  注意一個細節,如果在執行最後一次迴圈時,堆的大小在(2~k-1)之間(不足以取出k個),那麼整個赫夫曼樹的根的子節點個數就小於k,也就表明了最靠近根節點的位置反而沒有被排滿,這顯然不是最優解 。因此,我們應該在執行上述貪心演算法之前,補加一些額外的權值為0的葉子節點,使葉子節點的個樹滿足(n-1)%(k-1)=0。

  • 如果k=2,構建傳統的二叉赫夫曼樹。堆維護即可。
  • 如果k>2,當(n-1)%(k-1)!=0的時候會出現最後一次合併的結點數少於k個,需要增加空節點。
 1 // K階的赫夫曼樹
 2 #include<iostream>
 3 #include<cstdio>
 4
#include<cstring> 5 #include<queue> 6 #include<algorithm> 7 //#define ll long long 8 using namespace std; 9 10 //定義結點結構體 11 struct node { 12 int w,h; //w為權重,h為高度 13 node() {w = 0, h=0;} 14 node(int w, int h): w(w), h(h) {} 15 bool operator <(const node &a)const
{ //過載運算子,使w小的結點先出佇列 16 return a.w == w ? h>a.h : w>a.w; //優先考慮權值,其次考慮高度 17 } 18 }; 19 20 int ans; 21 22 priority_queue<node>q; //型別為node的優先佇列 23 24 int main() { 25 int n,k; 26 cin>>n>>k; 27 for(int i = 1; i <= n; i++) { 28 int w; 29 cin>>w; 30 q.push(node(w,1)); //預設h=11,這裡進行強制型別轉換 31 } 32 while((q.size()-1) % (k-1) !=0 ) { //需要新增空節點的情況 33 q.push(node(0,1)); //空節點預設w=0,h=1 34 } 35 while(q.size()>=k) { 36 int h = -1; 37 int w = 0; 38 for(int i = 1; i <= k; ++i) { //每次選出最小的k個結點 39 node t = q.top(); 40 q.pop(); 41 h = max(h,t.h); 42 w += t.w; 43 } 44 ans += w; 45 q.push(node(w, h + 1)); 46 } 47 48 cout<<ans<<endl; 49 cout<<q.top().h-1<<endl; 50 51 system("pause"); 52 return 0; 53 }