哈夫曼编码变式题,最优二叉树
哈夫曼树(最优二叉树)
给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。
每次去权值最小的两个,进行合并。这个可以用优先队进行实现的
利用优先队列来求解,每次从队列中取出最小值和次小值累加之后再入队,一直算到结点大小为1,即根结点为止。
#include <bits/stdc++.h> using namespace std; //哈夫曼树类问题 int main() { priority_queue< int, vector<int>, greater<int> > l; //最小值优先队列 int n, temp; cin >> n; //输入结点的个数n for (int i = 0; i < n; i++) { cin >> temp; //输入n个叶结点 l.push(temp); //叶结点入队操作 } int sum = 0; while(l.size() != 1) //当结点大小不为1时 { //取出队列中的最小的元素 int min1 = l.top(); l.pop(); //取出队列中的次最小的元素 int min2 = l.top(); l.pop(); //计算最小值和次最小值的权值 sum += min1 + min2; l.push(min1+min2); //出队列 } cout << sum << endl; return 0; }
那我们来看一个变式题把:
链接:https://ac.nowcoder.com/acm/contest/11212/D
来源:牛客网
溪染:六王毕,四海一;蜀山兀,阿房出。覆压三百余里,隔离天日。骊山北构而西折,直走咸阳。二川溶溶,流入宫墙。五步一楼,十步一阁;廊腰缦回,檐牙高啄;各抱地势,钩心斗角......
叁秋:还在背《阿房宫赋》啊。
溪染:(停下背书)明天就要测试了,难道不背?
叁秋:什么?明天要测试?我现在抱佛脚还来得及嘛?
溪染:也就十来篇文言文而已。哦,你比较笨,那估计来不及了
叁秋:(沉默几秒)救救孩子吧!
溪染:我想想办法,我把文言文先换成拼音,然后在考场用动作语言传给你怎么样?
叁秋:什么动作语言?
溪染:我们先规定一个规则好了,我用‘点头’和‘摇头’向你传递信息。
叁秋:然后呢?
溪染:比如用‘点头-摇头’代表′a′{'a'}′a′,用'点头-点头'代表′b′{'b'}′b′。
叁秋:我知道了!′c′{'c'}′c′就用'点头-点头-点头'表示
溪染:没救了,你是真的傻!如果这样,那我'点头-点头-点头-点头-点头-点头'表示什么?
叁秋:′bbb′{'bbb'}′bbb′可以,′cc′{'cc'}′cc′也行!!
溪染:对的,这样的规则就有歧义了,所以我们定义的任意2个规则,一个规则都不能是另一个的前缀,更不能相同!
叁秋:那这还不简单!我′a′{'a'}′a′用'点头-摇头'表示,′b′{'b'}′b′用'点头-点头-摇头'表示,′c′{'c'}′c′用'点头-点头-点头-摇头'表示,以此类推!
溪染:用你这个规则,我脖子可能就不在了!
溪染:我们应该找到一个规则,让我‘点头’and‘摇头’的次数和最少,这样的话即可以快速传递信息,被监考老师发现的几率也小一些。
叁秋:那我们改怎么规定规则呢?
溪染:现在我们把文言文转为拼音先
(过了一会儿)
溪染:我们已经知道到时候我该给你传递的信息了,我们稍加计算一下,就可以找到最好的规则了
叁秋:那怎么计算呢?
溪染:我都为你付出这么多了,怎么找到最好的规则就靠你自己了
(又过了一会儿)
叁秋实在不知道怎么计算,于是找到了你
输入描述:
一行字符串S,(1≤∣S∣≤5×106){S,(1\leq |S| \leq 5\times10^{6}})S,(1≤∣S∣≤5×106),表示需要传达的信息,有可能是文言文拼音,S{S}S中仅包含小写英文字母。
输出描述:
仅一行一个正整数表示问题的答案ans{ans}ans,表示传达信息所需最少的‘点头’and‘摇头’的次数之和。
示例3
说明
解释那么长,这点地方够吗?
当时看的时候真的没有想到是最优二叉树。哎,还是太菜了
#include<iostream> #include<algorithm> #include<queue> typedef long long ll; using namespace std; const int maxn=1e6+100; string s; int cnt[maxn]; int main(){ cin>>s; for(auto x:s){ cnt[x-'a']++; } priority_queue<ll,vector<ll>,greater<ll> >q; for(int i=0;i<26;i++){ if(cnt[i]) q.push(cnt[i]); } if(q.size()==1){ cout<<cnt[1]<<endl; } else{ ll ans=0; while(q.size()>1){ ll p1=q.top();q.pop(); ll p2=q.top();q.pop(); ans+=(p1+p2); q.push(p1+p2); } cout<<ans<<endl; } }