机智零崎不会没梗Ⅱ (哈夫曼编码、优先队列)

题目描述

你满心欢喜的召唤出了外星生物,以为可以变身超人拥有强大力量战胜一切怪兽,然而面对着身前高大的外星生物你一脸茫然,因为,你懂M78星云语吗?不过不用担心,因为零崎非常机智,他给出了关键性的提示:“讲道理,日语可是全宇宙通用语,所以为什么不试试和外星人讲日语呢?”

不过现在外星生物说的话都是“[email protected]#$%^&%#%I&!……”这样的东西,你要怎么转换成日语呢?

作位全宇宙通用的日语,自然有一套万能的转换算法,那就是Huffman编码转换!当然了这肯定不是普通的Huffman编码转换,而是根据不同的编码长度转换为不同的假名。

输入

第一行为一个整数t,接下来t行数据。1<=t<=100

每组输入数据为一个外星语字符串,为了表示方便,暂时使用大小写英文字母代替外星字母。字符串长度不超过2000

输出

对于每组数据,输出对应的二进制Huffman编码总长度

输入样例

2
abababac
abcdefg

输出样例

12
20题目来源:http://biancheng.love/contest/23/problem/D/index解题思路:在前面的哈夫曼树构造以及哈夫曼编码和解码时,已经讲述了哈夫曼编码的方式。现在我们需要得到哈夫曼编码的长度。回忆一下在解码过程中的算法,如果是0向左搜索,如果是1向右搜索。因此一个字符在一个构造好的哈夫曼树中都是居于叶子结点。意思就是树的各个分叉的最低端。因此每个字符的长度也就是该字符在哈夫曼树中的深度。希望能够理解这一点。在明白字符长度就是字符深度之后开始重新分析一下哈夫曼的树的构造过程。哈夫曼树的建立时通过对每个字符出现频率的统计来设计的,不同的出现频率会对应不同的深度也就是不同的长度。建立过程如下:1、取出频率最小的两个数,将这两个数按照最优二叉树的规则(左孩子<父节点<右孩子)得到父节点的值,将这个值放到上述频率中(相当于合并了两个最小的两个频率)2、按照上述规则进行反复合并与放回。3、得到哈夫曼树。通过上述分析得到每次需要得到两个最小的频率。怎样才能实现每次得到最小的两个频率呢?可以通过数组,但是每次都要排序,很麻烦也很费时间;可以使用第i个顺序统计量的随机算法,但是其复杂度也是很高的。那我们可以选用什么结构呢?这时候就到了之前讲过的优先队列使用优先队列的过程:1、首先需要统计字符串中每个字母的出现频率,由于题目中已经简化问题,将字符限制在小写字符a到小写字符z,2、初始化a到z的出现频率为0,所以在统计完频率频率之后需要借助频率不为0这一重要条件将出现的字符push进入队列。3、每次取出最小的两个相加之后再放入优先队列。退出循环的条件为优先队列为空。下面给出本题代码:
#include <bits/stdc++.h>
#define max_size 10010

using namespace std;

typedef long long LL;
char c[max_size];
long long f[max_size];

priority_queue<LL ,vector<LL>,greater<LL> >q;//建立小顶堆;
long long n,ans;
int main()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        string s;
        cin>>s;
        getchar();
        memset(f,0,sizeof(f));
        int lens=s.size();
        for(int i=1; i<=lens; i++)
        {
            c[i]=s[i-1];
            f[c[i]]++;
        }

        while(!q.empty())
            q.pop();
        for(int i=65; i<=122; i++)
        {
            if(f[char(i)]>0)
            {
                sum++;
                q.push(f[char(i)]);
            }
        }
        ans=0;
        while(q.size()>1)
        {
            LL a=q.top();
            q.pop();
            LL b=q.top();
            q.pop();
            ans+=(a+b); // 因为编码长度和其在树中的层数相关
            q.push(a+b);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

可以发现在前面的统计频率不仅仅是为了统计频率,同时也是实现了将其push进优先队列。

下面给出按照哈夫曼树解决问题的代码,可以比较两种方法的优缺点:

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cmath>
  4 #include <cstdlib>
  5 #include <algorithm>
  6 #include <iomanip>
  7 #include <cstring>
  8 #include <string>
  9 #define INF 0xFFFFFF
 10 using namespace std;
 11
 12 typedef struct
 13 {
 14     int parent[1005];
 15     int lchild[1005];
 16     int rchild[1005];
 17     int weight[1005];
 18 } Htree;
 19
 20 int createHt(Htree &ht)
 21 {
 22     int n = 0;//length;
 23     int min1,min2;
 24     int lchild,rchild;
 25     memset(ht.parent,-1,sizeof(ht.parent));
 26     memset(ht.lchild,-1,sizeof(ht.lchild));
 27     memset(ht.rchild,-1,sizeof(ht.rchild));
 28     memset(ht.weight,0,sizeof(ht.weight));
 29
 30     string str;
 31     cin>>str;
 32     int uppercase[26], lowercase[26];
 33     memset(uppercase, 0,sizeof(uppercase));
 34     memset(lowercase, 0, sizeof(lowercase));
 35     for(int i = 0; i<str.length(); i++)
 36     {
 37         if(str[i]<91)//uppercase
 38             uppercase[str[i]-65]++;
 39         else
 40             lowercase[str[i]-97]++;
 41     }
 42     for(int i=0; i<26; i++)
 43     {
 44         if(uppercase[i]!=0)
 45         ht.weight[n++] = uppercase[i];
 46     }
 47     for(int i=0; i<26; i++)
 48     {
 49         if(lowercase[i]!=0)
 50         ht.weight[n++] = lowercase[i];
 51     }
 52
 53     for(int i=n; i<2*n-1; i++)
 54     {
 55         min1=min2=INF;
 56         lchild=rchild=-1;
 57         for(int j=0; j<i; j++)
 58         {
 59             if(ht.parent[j]==-1)
 60             {
 61                 if(ht.weight[j]<min1)
 62                 {
 63                     min2=min1;
 64                     rchild=lchild;
 65                     min1=ht.weight[j];
 66                     lchild=j;
 67                 }
 68                 else if(ht.weight[j]<min2)
 69                 {
 70                     min2=ht.weight[j];
 71                     rchild=j;
 72                 }
 73             }
 74         }
 75         ht.weight[i]=ht.weight[lchild]+ht.weight[rchild];
 76         ht.lchild[i]=lchild;
 77         ht.rchild[i]=rchild;
 78         ht.parent[lchild]=ht.parent[rchild]=i;
 79     }
 80     return n;
 81 }
 82
 83 int main()
 84 {
 85     int T;
 86     Htree ht;
 87     long long result;
 88     int level;
 89     cin>>T;
 90     while(T--)
 91     {
 92         int len = createHt(ht);
 93         result = 0;
 94         for(int i = 0; i<len; i++)
 95         {
 96             level = 0;
 97             int j = i;
 98             while(ht.parent[j]!=-1)
 99             {
100                 level++;
101                 j=ht.parent[j];
102             }
103             result+=level*ht.weight[i];
104         }
105         printf("%lld\n", result);
106     }
107 }


时间: 2024-10-06 06:41:05

机智零崎不会没梗Ⅱ (哈夫曼编码、优先队列)的相关文章

机智零崎不会没梗Ⅲ (摊还分析)

题目描述 零崎总是说自己有一百种梗可玩,然而其实都是假的,是化学成分的,是特技.想要给摊还分析加个梗,实在是不好想,因为这个内容并不是什么算法,而是算法分析. 不过既然还得考,那么没有办法…… “势能法”是摊还分析中一种比较简单常用的方法,而且容易理解.现在零崎有K个硬币,规定每次“翻动”操作只能从最右侧开始翻转硬币,且如果把一个硬币从正面向上翻到背面向上,则需要对其左侧相邻的那个硬币也执行“翻动”操作(翻至最左则结束).定义硬币组的势为硬币中正面向上的硬币的个数.现在要求你求出从某个给定的硬币

3D计算机图形学零起点全攻略(转)

3D计算机图形学零起点全攻略 这篇文章不包含任何技术知识,但我的希望它能指明一条从零开始通往3D领域的成功之路.我将罗列我看过的相关经典书籍作为学习文献,阅读规则是每进入下个内容,我都会假设已经完成前面全部的文献研习内容.相信若能按照这条路走到最后,会有所进益. 完成整部分内容需要具备基础: 英语:CET4以上 数学:精通数字加减乘除法. 物理:基本力学. 计算机:了解电脑的基本知识,熟练使用Windows. 电脑配置: CPU:双核1.5以上 显卡:NVIDIA GeForce8400G MS

学习Word2vec

有感于最近接触到的一些关于深度学习的知识,遂打算找个东西来加深理解.首选的就是以前有过接触,且火爆程度非同一般的word2vec.严格来说,word2vec的三层模型还不能算是完整意义上的深度学习,本人确实也是学术能力有限,就以此为例子,打算更全面的了解一下这个工具.在此期间,参考了[1][2][3]的博文,尤其以[1]的注释较为精彩.本文不涉及太多原理,想要对word2vec有更深入的了解,可以阅读Mikolov在2013年的两篇文章[4][5].同时文献[6]对word2vec中的模型和一些

huffman编码【代码】

哈夫曼编码应该算数据结构"树"这一章最重要的一个问题了,当时大一下学期学的时候没弄懂,一年后现在算是明白了. 首先,讲讲思路. 正好这学期在学算法,这里面就用到了贪心算法,刚好练练手. 整个问题有几个关键点: 1,首先是要思考怎么样存下从txt中读取的一个所有字符中每种字符出现的次数,首先想到的应该是结构体数组,再仔细想想不对呀,时间复杂度太高了,每次判断一个字符都对知道它属于结构体数组中的哪一个,那要是txt中有很多字符,那光这个就得花好多时间.然后想想,如果读取的字符如果只有那25

Python 的经典入门书籍

实python非常适合初学者入门,上手很容易.我就是完全通过网上资源学了python的.最大的是3点经验:1.找一本浅显易懂,例程比较好的教程,从头到尾看下去.不要看很多本,专注于一本.把里面的例程都手打一遍,搞懂为什么.2.去找一个实际项目练手.我当时是因为要做一个网站,不得已要学python.这种条件下的效果比你平时学一门新语言要好很多.所以最好是要有真实的项目做.可以找几个同学一起做个网站之类.3.最好能找到一个已经会python的人.问他一点学习规划的建议,然后在遇到卡壳的地方找他指点.

Wavelet Transform &amp; Image Compression

/***********************Wavelet  Transform出现背景***********************/ 图像编码在保证一定重构质量的前提下,通过取出图像中的各种冗余,以尽量少的比特数来表示图像. 图像编码长期以来主要利用离散余弦变换(DCT)作为变换编码的主要技术,然而存在如下一些问题:(1)利用DCT变换存在明显的方块效应,(2)不适用于对非平稳信号,(3)要进一步提高压缩性能很困难. 小波变换是在短时傅里叶变换的基础上发展起来的一种新型变换方法,他是一种

ACM程序设计学期总结

心路历程         大一的时候就跟着费老师学了近一个学期的ACM程序设计课程,但是由于种种出自于自己的原因,当时并没有完全塌下心去学,只是学到了些基础的东西,做的题太少,当然也并没有深入理解.大二又有了这个机会,自然不能放过,又认认真真重新学了一遍.         虽然这学期这门课一共只学了四个专题,但是对我来说,收获非常大,正像费老师说的:acm是思维的体操.它不停留在基本的语言层面,而是专注于解决各种实际问题的有一定难度的算法,它虽然会用到各种思想.各种工具,但是它不拘泥于某种固定的

RTP传输JPEG图片到VLC实时播放(代码)

一.环境是ubuntu 二.采集视频. 我这里采集的是YUYV422,然后可以通过两种方式转化,1.yuyv422->yuv420->jpeg->rtp->vlc(yuv420解码).2.yuyv422->rgb->jpeg->rtp->vlc(yuv420解码) V4L2编程可以参考:V4L2编程 YUYV422转RGB参考:YUYV422TORGB 三.代码下载 包含jpeg-9a库,库安装百度就ok. 传输代码看readme. 点击这里下载源代码 四.

字符编码、字符存储、字符转换及工程中字符的使用

字符编码.字符存储.字符转换及工程中字符的使用 版本控制 版本 时间(北京时间) 作者 备注 V1.0 2016-05-13 施小丰 创建本文.第七章工程总结尚未完成 一.          前言 1.        目的 本文主要用于整理字符相关知识,包括字符编码.字符存储.行业标准.文件读写.工程注意事项等涉及字符相关的内容, 从而在实际工程中更好地设计和使用字符.更快地解决字符问题. 2.        适用范围 本文标题是"Windows C++字符编码.存储.转换大全", 但