题解:本题为可变基哈夫曼编码,普通的哈夫曼编码为二叉树,即R=2.
例如:输入3 4 5 7 8 1 5,表示基数R3,字符个数N=4,每个字符的频率为A:5、B: 7、C:8、D:15构建的哈夫曼树
算法设计:
1、先补充虚拟字符,使N=k*(R-1)+R,k为整数,即(N-R) % (R-1) =0;
2、每个节点都包含frequency、va、id这3个域,分别表示频率、优先值、序号;
3、定义优先级。频率越小越优先,如果频率相等,则值越小越优先;
4、将所有节点都加入优先队列。
5、构建可变基哈夫曼树。
6、进行可变基哈夫曼树编码。
#include <bits/stdc++.h>
using namespace std;
struct node {
int freq, va, id;
node(int x = 0, int y = 0, int z = 0) {
freq = x, va = y, id = z;
}
bool operator <(const node &b) const {
if (freq == b.freq) {
return va > b.va;
}
return freq > b.freq;
}
};
const int maxn = 100;
int R, N; //基数、字母个数
int n, c; //补虚拟字母后的个数n,新生成字母编号c
int fre[maxn], father[maxn], code[maxn];
priority_queue<node>Q;
int main() {
int cas = 1;
while (cin >> R && R) {
cin >> N;
memset(fre, 0, sizeof(fre));
int total = 0;
for (int i = 0; i < N; i++) {
cin >> fre[i];
total += fre[i];
}
n = N;
while ((n - R) % (R - 1) != 0) //补虚拟节点
n++;
while (!Q.empty()) //优先队列清空
Q.pop();
for (int i = 0; i < n; i++) //所有节点入队
Q.push(node(fre[i], i, i));
c = n; //新合成节点编号
int rec = 0; //统计所有频率和值
while (Q.size() != 1) { //剩余一个节点停止合并
int sum = 0, minva = n;
for (int i = 0; i < R; i++) {
sum += Q.top().freq; //统计频率和
minva = min(Q.top().va, minva); ///求最小优先值
father[Q.top().id] = c; //纪录双亲
code[Q.top().id] = i; //纪录编码
Q.pop();//出队
}
Q.push(node(sum, minva, c));
c++;
rec += sum;
}
c--;
printf("%d集合:平均长度 %0.2f\n", cas, 1.0 * rec / total);
for (int i = 0; i < N; i++) {
int cur = i;
string s;
while (cur != c) {
s.push_back(code[cur] + '0');
cur = father[cur];
}
reverse(s.begin(), s.end()); //翻转代码
cout << " " << char('A' + i) << ":" << s << endl;
cout << endl;
cas++;
}
}
return 0;
}