7-88 哈夫曼编码 (30分)
输入样例:
7
A 1 B 1 C 1 D 3 E 3 F 6 G 6
4
A 00000
B 00001
C 0001
D 001
E 01
F 10
G 11
A 01010
B 01011
C 0100
D 011
E 10
F 11
G 00
A 000
B 001
C 010
D 011
E 100
F 101
G 110
A 00000
B 00001
C 0001
D 001
E 00
F 10
G 11
输出样例:
Yes
Yes
No
No
思路:
1. 根据输入,将字符出现的频率,加入优先队列(从小到大)中,方便后面计算。并使用map对字符进行标记,标记每个字符对应的频率。
2. 计算最优的树的带权路径长度(WPL),也就是最小的带权路径长度。这里主要利用构造哈夫曼树的方法,每次从优先队列中取出两个最小的数,进行加权,再将计算结果放到优先队列中。直到队列中只剩一个元素时,循环结束,计算最优带权长度成功。
3. 最后就进行判断,每组数据符不符合条件了。条件1 判断每组的带权路径长度是否和最优路径长度相等; 条件2 判读每个字符的编码方式是否合法,即每个字符之间的编码是否有歧义,比如A:0000,B:00就有歧义,当遇到0000时不知道表示的是A,还是BB。我使用的方法是使用substr函数进行判断,即如果一个短的字符串是另一个长的字符串的前缀,则就不合法,进行标记。
(ps:建议不熟悉哈夫曼的读者,先去了解哈夫曼树及构造算法,该题自然也就变得简单了)
具体AC代码如下:
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,x,m,sum=0;
char c;
cin>>n;
getchar();
map<char,int> mp; //用于标记字符,方便处理
priority_queue<int,vector<int>,greater<int> > q; //按照从小到大排序
for(int i=0; i<n; i++){
cin>>c>>x;
mp[c]=x;//标记字符对应的编号,对字符进行编号
q.push(x);
}
while(q.size()!=1){ //计算树的最小带权路径
int a=q.top();
q.pop();
int b=q.top();
q.pop();
sum=sum+(a+b);
q.push(a+b);
}
cin>>m;
while(m--){
int sum2=0,flag=0;
string s[100];
for(int i=0; i<n; i++){
cin>>c>>s[i];
for(int j=0; j<i; j++){
int d=min(s[i].size(), s[j].size());
if(s[i].substr(0,d)==s[j].substr(0,d)) flag=1;//检测一个字符串是不是另一个字符串的前缀
}
sum2+=mp[c]*s[i].size();
}
if(!flag&&sum==sum2) cout<<"Yes\n";
else cout<<"No\n";
}
return 0;
}
欢迎大家批评改正!!!