哈夫曼树学习小记
2017-08-24 15:37
197 查看
B组又现我不会的神奇东西了……
你需要把这n个元素放在一棵二叉树的叶子节点上,规定每个元素的代价为它所在叶子节点的深度乘上它的值,哈夫曼树就是使总代价最小的这样一棵树。
理论上当然是O(n)的,因为判断非常少,但是假设分数段非常非常多,我们知道最坏情况要把每个if都走一遍,在大数据中这非常慢。
然而事实上成绩的分布并不是均匀的,有些分数段人多,有些分数段人少,我们可以通过改变if的先后顺序来起到优化的目的。
举个例子,下面的图片盗自这里。
第一种构造方式:
![](http://img.blog.csdn.net/20170824151513204?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQ29sZF9DaGFpcg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
第二种构造方式:
![](http://img.blog.csdn.net/20170824151553119?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQ29sZF9DaGFpcg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
所以如果我们预先抽取一些数据知道了各个分数段的频数,把频数看作权值,建一棵哈夫曼树,按照哈夫曼树的顺序去判断,就可以大大优化时间。
设fi,j表示现在已经做了前i个元素,还空出来j个叶子节点的最小代价。
第一种转移显然,就是把第i+1个元素放到一个叶子节点上,fi+1,j−1=fi,j。
第二种转移就是把当前剩下的叶子节点都再生成出两个节点来,代价是把当前的剩下元素全部加深了一层,所以fi,2∗j=fi,j+∑nk=i+1ak。
Ans=min(fn,k)
哈夫曼树的代价就是合并果子的代价。
用个堆维护是O(n log n)的。
Code:
定义:
现在有n个元素,每个元素有一个值。你需要把这n个元素放在一棵二叉树的叶子节点上,规定每个元素的代价为它所在叶子节点的深度乘上它的值,哈夫曼树就是使总代价最小的这样一棵树。
运用:
据说哈夫曼树是一棵最佳判定树,什么意思呢?举一个实例来看看。问题:
给出一群学生的成绩,要你判断每个学生的成绩情况(不同分数段有不同的评价)。解法:
这是一道信息学入门题,一贯的做法是打一坨if。理论上当然是O(n)的,因为判断非常少,但是假设分数段非常非常多,我们知道最坏情况要把每个if都走一遍,在大数据中这非常慢。
然而事实上成绩的分布并不是均匀的,有些分数段人多,有些分数段人少,我们可以通过改变if的先后顺序来起到优化的目的。
举个例子,下面的图片盗自这里。
第一种构造方式:
第二种构造方式:
所以如果我们预先抽取一些数据知道了各个分数段的频数,把频数看作权值,建一棵哈夫曼树,按照哈夫曼树的顺序去判断,就可以大大优化时间。
构造:
首先需要明白的是,权值越大的深度肯定要越低,这个贪心显然。动态规划做法:
先对元素从大到小排序。设fi,j表示现在已经做了前i个元素,还空出来j个叶子节点的最小代价。
第一种转移显然,就是把第i+1个元素放到一个叶子节点上,fi+1,j−1=fi,j。
第二种转移就是把当前剩下的叶子节点都再生成出两个节点来,代价是把当前的剩下元素全部加深了一层,所以fi,2∗j=fi,j+∑nk=i+1ak。
Ans=min(fn,k)
贪心做法:
就和合并果子一毛一样,一开始有n堆果子,每堆果子的大小是元素的值。每次选最小的两堆果子合并,直到剩下一堆果子。每次合并可以看作将两堆果子上的元素深度加一,和dp做法不同的是,这是倒着做的,先确定了权值小的元素,然后将它们不断加深,自己感受一下即可。哈夫曼树的代价就是合并果子的代价。
用个堆维护是O(n log n)的。
Code:
#include<set> #include<cstdio> #define fo(i, x, y) for(int i = x; i <= y; i ++) using namespace std; const int N = 100005; multiset<int> s; int T, n, a ; long long sum; int main(){ for(scanf("%d", &T); T; T --) { s.clear(); sum = 0; scanf("%d", &n); fo(i, 1, n) scanf("%d", &a[i]), s.insert(a[i]); fo(i, 1, n - 1) { int x = *s.begin(); s.erase(s.begin()); int y = *s.begin(); s.erase(s.begin()); sum += x + y; s.insert(x + y); } printf("%lld\n", sum); } }
相关文章推荐
- 8.2JAVA学习——List以及Set的小记
- 学习日志---哈夫曼树相关算法
- yarn 学习 小记
- Cipolla算法学习小记
- 使用 Git 进行问题定位学习小记
- JS的学习小记
- 数论学习小记 其之三 Gcd与Lcm
- js 正则学习小记之匹配字符串字面量
- LightOJ 1027 A Dangerous Maze 概率期望学习小记
- 数据结构 学习笔记(六):树(下):堆,哈夫曼树和哈夫曼编码,集合及运算
- struts-helloapp 学习小记
- linux学习小记02-linux从入门到精通系统管理篇,第6章 文件目录管理
- (2014.07.14-2014.07.20)七天的学习小记
- 2018.04.3 Java学习小记
- Shark工作流解决方案的学习小记
- Monkey API学习与脚本编写小记
- ExtJs 4.x 学习小记:Ext.PagingToolbar的使用
- html标签选择器的使用选择(学习小记)
- uboot学习小记
- C++ Primer Plus(第6版学习小记)-开篇