2012-11-08 20:19:57Morris

[ZJ][三年後修訂] d371. 3. Huffman 編碼中的編碼效能問題

內容 :

為了用0 與1 的位元串表示電腦中所需使用的符號,常見的編碼法可分為兩大類:固定長度編碼法與非固定長度編碼法。
固定長度編碼法指的是,將每個符號用相同長度的位元串表示。
例如,要以二進位的0 與1 位元串表示{a, b, c, d, e } 5個符號
每個符號最少需要以3 個位元來編碼成{000, 001, 010, 011, 100}。
但如果我們已知這些符號在多數資料中出現的頻率不同,就可用非固定長度編碼法更有效地表示這些資料
而Huffman 編碼法就是非固定長度編碼法中最常使用的方法。
例如,已知{a, b, c, d, e}這5 個符號的出現頻率由大至小如表一所示。


則Huffman 編碼法利用建立二元樹的方式,首先將每個符號以一個自由節點(freenode)表示
這些自由節點的權重(weight)即是符號的頻率。
接著,從自由節點中找出權重最小的兩個節點,為這兩個節點做一個父節點,此父節點的權重則為這兩個子節點的權重和。
之後,將父節點加入自由節點的行列,並將兩個子節點從自由節點的行列中去除。
接著,重覆選取兩個權重最小的節點,造出父節點並更新自由節點的過程,直到最後只剩一個自由節點為止。
當得到這棵編碼樹後,我們就可以從二元樹的根結點(root node)開始往下走
每往左子樹(left subtree)走一層就給定一個位元的編碼0
每往右子樹(right subtree)走一層就給定一個位元的編碼1
依此方式逐層往下走並同時編碼直到走到葉節點(leaf node)為止。
圖一、二所示即為依表一頻率表所建造的不同Huffman 編碼樹。


雖然圖一和圖二的編碼結果不同,但檢視其編碼效能,如表二所示,可發現這兩個編碼所得到的總位元數是相同的:
16+24+16+16+16+16=32+16+16+12+12=88。


請撰寫一個程式,根據已知的頻率表(尚未排序過),計算以Huffman 編碼後的總位元數。

輸入說明 :

第一行為一個整數n,代表共有幾個符號。(n<=2k,k 為正整數,1<k<=16)。
接下來的n 個數字代表這n 個符號的頻率,每個數字為介於1 與216 之間的正整數,每兩個數字以一個空格隔開。

輸出說明 :

對每一個輸入的頻率表,計算以Huffman 編碼後的總位元數。

範例輸入 :

5
16 8 8 4 4

範例輸出 :

88

提示 :

出處 :

97全國能力縣賽 (管理:pcshic)


/**********************************************************************************/
/*  Problem: d371 "3. Huffman 編碼中的編碼效能問題" from 97全國能力縣賽*/
/*  Language: CPP (1293 Bytes)                                                    */
/*  Result: AC(4ms, 771KB) judge by this@ZeroJudge                                */
/*  Author: morris1028 at 2012-11-08 20:14:38                                     */
/**********************************************************************************/


#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
struct ele {
    int nd;
    long long v;
};
struct NODE {
    int l, r;
    long long v;
    char c;
};
struct cmp {
    bool operator() (ele a, ele b) {
        return a.v > b.v;
    }
};
NODE ND[99999];
long long ans;
void dfs(int nd, int dep) {
    if(!ND[nd].l && !ND[nd].r)
        ans += dep*ND[nd].v;
    if(ND[nd].l)
        dfs(ND[nd].l, dep+1);
    if(ND[nd].r)
        dfs(ND[nd].r, dep+1);
}
int main() {
    int n, A[99999];
    int i;
    while(scanf("%d", &n) == 1) {
        priority_queue<ele, vector<ele>, cmp> pQ;
        ele l, r, e;
        for(i = 1; i <= n; i++) {
            scanf("%d", &A[i]);
            e.nd = i, e.v = A[i];
            ND[i].l = 0, ND[i].r = 0;
            ND[i].v = A[i];
            pQ.push(e);
        }
        int size = n;
        while(pQ.size() > 1) {
            l = pQ.top(), pQ.pop();
            r = pQ.top(), pQ.pop();
            size++;
            e.nd = size, e.v = l.v+r.v;
            ND[size].l = l.nd;
            ND[size].r = r.nd;
            ND[size].v = l.v+r.v;
            pQ.push(e);
        }
        ans = 0;
        dfs(size, 0);
        printf("%lld\n", ans);
    }
    return 0;
}