原题链接
解题思路
题目给了提示,不一定要每次都建立哈夫曼树,而是通过比较前缀码的长度是否为最优。
那么对于给定的一组字符和频率(因为输入输出字符都是按顺序的,所以没必要存这些字符),可以不建哈夫曼树的得到其WPL,主要是利用优先队列模拟小顶堆,只要优先队列中的元素个数大于1,就从中取出最小的两个元素,WPL加上这两个元素的和,然后再将这两个元素的和入队。
为什么是这样的?来自于合并果子问题。
如何判断一个同学的结果是否正确有两个地方:
-
判断其编码的wpl是否等于最优WPL
得到WPL后,针对每个同学的结果,在输入时,记录下来编码(为了判断是否是前缀码,无二义性的),然后计算该同学编码结果的wpl,wpl每次都加上频率*编码长度。 -
判断这些编码是否是无二义性的前缀码
将编码按字符串长度从小到大排序,然后依次看每一个编码是否是后面编码的一部分即可
源代码
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 100;
priority_queue<int, vector<int>, greater<int> > q;//优先队列替代小顶堆
int data[maxn];//存放各个元素的频率
string prefix[maxn];//存放各个前缀
int n, WPL = 0;
bool cmp(string s1, string s2){
return s1.length() < s2.length();
}
bool check(){
char ch;
int wpl = 0;
bool flag = true;//答案是否正确
for(int i=0; i<n; i++){
cin>>ch;
cin>>prefix[i];
wpl += data[i]*prefix[i].length();
}
if(wpl != WPL)
flag = false;
//下面继续判断是否为前缀码
//按长度排序
sort(prefix, prefix+n, cmp);
for(int i=0; i<n-1; i++){
for(int j=i+1; j<n; j++){
if(prefix[i] == prefix[j].substr(0, prefix[i].length())){
flag = false;
break;
}
}
}
return flag;
}
int main(){
int m;
cin>>n;
getchar();
char ch;
for(int i=0; i<n; i++){
cin>>ch;
cin>>data[i];
getchar();
q.push(data[i]);
}
//求出正确的WPL
while(q.size() > 1){
int top1 = q.top();
q.pop();
int top2 = q.top();
q.pop();
WPL += (top1+top2);
q.push(top1+top2);
}
// printf("%d\n", WPL);
cin>>m;
for(int i=0; i<m; i++){
if(check())//WPL是最短且是前缀码
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
return 0;
}