原題鏈接
解題思路
題目給了提示,不一定要每次都建立哈夫曼樹,而是通過比較前綴碼的長度是否爲最優。
那麼對於給定的一組字符和頻率(因爲輸入輸出字符都是按順序的,所以沒必要存這些字符),可以不建哈夫曼樹的得到其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;
}