• Huffman树总结


    其实我觉得这个东西还是比较玄学,主要是实现起来感觉不是难,重要的是学会如何建模.

    定义

    构造一棵树使一棵树中点的权值与深度的乘积和最小
    (Minsum_{i}val[i]*dep[i](i∈V))
    那么我们如何构造一棵这样的树呢?显然对于每一个点,只有权值是给出的,我们yy一下就发现,只有当权值大的树的深度尽量小那么才能够保证权值和比较小.所以我们可以用优先队列|堆完成这个任务
    但是一般的题目不会让你写Huffman树的裸题,那么显然,构造出来的树的分叉也不一定只有两个,所以我们要掌握很多东西.

    结论1

    若一颗Huffman树是满k叉树,那么显然(n-1)%(k-1)必然为0.

    这个的证明其实还是比较简单,因为你将根节点除去后的图必然有k的倍数个分叉,这才满足是一个满k叉树

    结论2

    权值越大的放到深度小的才能够构成Huffman树.

    这个就是依据Huffman树的定义来的.

    模板

    讲了这么多,那么Huffman树究竟有没有一个固定的模板呢?答案是有的

    struct node{
        int val,dep;
        bool operator<(node b){
            return val>b.val || val==b.val && dep>b.dep;//显然深度大的如果不先合并它就会变得越来越大!
        }
    }
    priority_queue<node>q;
    void Huffman(int n,int k){//一共有n个点,k叉Huffman树
        ll sum=0;
        if((n-1)%(k-1))sum=(k-1)-(n-1)%(k-1);//需要补充的节点
        for(int i=1;i<=sum;i++){
            node need;
            need.dep=1;need.val=0;//补充val为0的节点显然对答案没有影响.
            q.push(need);
        }
        sum+=n;
        while(sum>1){//还没到根节点
            node a;int mx,now=0;
            for(int i=1;i<=k;i++){
                node now=q.top();mx=max(mx,q.dep);
                now+=q.val();q.pop();
            }
            a.val=now;ans+=now;//合并后的权值
            a.dep=mx+1;q.push(a);//合并后的深度,搞好后放进堆
            sum-=(k-1)//搞好了k-1个节点
        }
        //最后的合并的结果存储在ans里面
    }
    

    例题1 NOIP2004 合并果子

    原题


    这道题目显然是Huffman树的裸题,我们每次搞两个数合并,然后合并出来的树权值变大,那么最优解就满足Huffman树的定义,那么就可以这么搞.

    #include<stdio.h>
    #include<stdlib.h>
    #include<queue>
    #define re register
    #define ll long long
    using namespace std;
    int n;
    priority_queue<int >q;
    int main(){
    	scanf("%d",&n);
    	for(re int i=0;i<n;i++){
    		int a;scanf("%d",&a);
    		q.push(-a);
    	}
    	int ans=0;
    	while(q.size()>1){
    		int del1=q.top();q.pop();
    		int del2=q.top();q.pop();
    		ans-=del1+del2;
    		q.push(del1+del2);//这里维护的是2叉
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    例题2 NOI2015 荷马史诗

    原题


    显然为了使总长度最小,且每一个点有属于自己的val,很容易想到Huffman树,那么我们就可以理所当然的构造一棵,然后在这颗Huffman树里寻找出需要的答案.

    #include<stdio.h>
    #include<algorithm>
    #include<queue>
    #define re register
    #define ll long long
    using namespace std;
    struct node{
    	ll dep,val;
    	bool operator<(node b)const{
    		return val>b.val || val==b.val && dep>b.dep;
    	}
    };
    priority_queue<node>q;
    ll n,k;
    int main()
    {
    	scanf("%lld%lld",&n,&k);
    	for(re ll i=1;i<=n;i++){
    		node a;a.dep=1;
    		scanf("%lld",&a.val);
    		q.push(a);
    	}
    	ll bu=0;
    	if((n-1)%(k-1)==0);
    	else bu+=k-1-(n-1)%(k-1);
    	for(re ll i=1;i<=bu;i++){
    		node a;a.dep=1;a.val=0;
    		q.push(a);
    	}
    	bu+=n;ll ans=0;
    	while(bu>1){
    		node a;ll redep=0,nowa=0;
    		for(re ll i=1;i<=k;i++){
    			node now=q.top();nowa+=now.val;
    			redep=max(redep,now.dep);q.pop();
    		} 
    		bu-=k-1;
    		ans+=nowa;a.dep=redep+1;
    		a.val=nowa;
    		q.push(a);
    	}
    	printf("%lld
    %lld
    ",ans,q.top().dep-1);
    	return 0;
    }
    

    习题(待补充)

    总结

    那么这一类题目在题面中一般会告诉我们k和要我们求出一个最小值,那么这就是Huffman树的适用范围,然后更多的话就需要在题目中自行探索了.

  • 相关阅读:
    令Django 视图有默认 login_required
    sql语句 case
    java进制转换
    倒水问题
    全排列
    数据库范式
    操作系统——磁盘设备管理
    Windows系统安装MySQL
    Java题库——Chapter16 JavaFX UI组件和多媒体
    Java题库——Chapter15 事件驱动编程和动画
  • 原文地址:https://www.cnblogs.com/cjgjh/p/9511926.html
Copyright © 2020-2023  润新知