哈夫曼编码变式题,最优二叉树

哈夫曼树(最优二叉树)

给定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,(1S5×106),表示需要传达的信息,有可能是文言文拼音,S{S}S中仅包含小写英文字母。

输出描述:

仅一行一个正整数表示问题的答案ans{ans}ans,表示传达信息所需最少的‘点头’and‘摇头’的次数之和。
示例1

输入

复制
aaaaaa

输出

复制
6

说明

‘a’用'点头'代表即可
示例2

输入

复制
abcabc

输出

复制
10

说明

‘a’用'点头'代表
‘b’用‘摇头-点头’代表
‘c’用‘摇头-摇头’代表
示例3

输入

复制
havefuninthegame

输出

复制
54

说明

解释那么长,这点地方够吗?


当时看的时候真的没有想到是最优二叉树。哎,还是太菜了
#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;
    }
}

 

posted @ 2021-06-20 11:33  lipu123  阅读(547)  评论(0)    收藏  举报