习题:荷马史诗(哈夫曼树&贪心)

题目

传送门

思路

如果我们最后编码的答案建成一个trie树,

一条路径上除了末尾可以是一个编码的结尾

这条路径上的任何一个点都不可能是一个编码的结尾

因为需要满足一个编码不是另一个编码的前缀

如果我们视每一个点的点权为这个点出现的次数

每一个单词的编码为根节点到叶子节点上路径上的字符

也就是它的长度就是它的深度,也就是问题1可以表示为

\(ans=\sum_{i=1}^{n}dep_i*w_i\)

需要最小,

\(w_i\)是给定的,我们能做的只是调整每个编码的长度,

也就是末端节点再树上的深度

感性理解一下

就是\(w_i\)越大的点它的\(dep_i\)越小

\(w_i\)越小的点它的\(dep_i\)越大

我们还需要使最大的长度最短

很明显的一点,每一个节点的分支一定是尽可能多的

所以我们可以将\(w\)从小到大排序

用优先队列来维护

取出前k个点,将他们的权值累加,建一个新点,再将这个点重新塞回优先队列中

如此反复,直至优先队列中只剩下1个点

很明显权值越大的距离根节点的距离一定越近

如果是我们建的是虚点呢?也无妨

因为我们还需要保证一个编码不是另一个编码的前缀

此方法也可称为huffman编码

代码

#include<iostream>
#include<queue>
using namespace std;
struct node
{
    int dep;
    long long w;
    friend bool operator < (const node &a,const node &b)
    {
        if(a.w==b.w)
            return a.dep>b.dep;
        return a.w>b.w;
    }
}a[100005];
int n,k;
long long ans;
node cnt[15];
priority_queue<node> q;
int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        long long w;
        cin>>w;
        q.push((node){0,w});
    }
    while((n-1)%(k-1))
    {
        n++;
        q.push((node){0,1ll*0});
    }
    while(q.size()!=1)
    {
        for(int i=1;i<=k;i++)
        {
            cnt[i]=q.top();
            q.pop();
        }
        node t;
        t.dep=0;
        t.w=0;
        for(int i=1;i<=k;i++)
        {
            t.w+=cnt[i].w;
            t.dep=max(t.dep,cnt[i].dep);
        }
        t.dep++;
        q.push(t);
        ans+=t.w;
    }
    cout<<ans<<' '<<q.top().dep;
    return 0;
}

原文地址:https://www.cnblogs.com/loney-s/p/12076863.html

时间: 2024-10-09 10:01:00

习题:荷马史诗(哈夫曼树&贪心)的相关文章

[BZOJ4198][Noi2015]荷马史诗 哈夫曼树

4198: [Noi2015]荷马史诗 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 1471  Solved: 779[Submit][Status][Discuss] Description 追逐影子的人,自己就是影子. --荷马 Allison 最近迷上了文学.她喜欢在一个慵懒的午后,细细地品上一杯卡布奇诺,静静地阅读她爱不释手的<荷马史诗>.但是由<奥德赛>和<伊利亚特>组成的鸿篇巨制<荷马史诗>实在

NOI2015 荷马史诗 [哈夫曼树]

Description 一部<荷马史诗>中有n种不同的单词,从1到n进行编号.其中第i种单 词出现的总次数为wi.Allison 想要用k进制串si来替换第i种单词,使得其满足如下要求: 对于任意的 1 ≤ i, j ≤ n , i ≠ j ,都有:si不是sj的前缀. 现在 Allison 想要知道,如何选择si,才能使替换以后得到的新的<荷马史诗>长度最小.在确保总长度最小的情况下,Allison 还想知道最长的si的最短长度是多少? 一个字符串被称为k进制字符串,当且仅当它的

bzoj 4198 [Noi2015]荷马史诗——哈夫曼树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4198 学习一下哈夫曼树.https://www.cnblogs.com/Zinn/p/9400381.html #include<iostream> #include<cstdio> #include<cstring> #include<queue> #define ll long long using namespace std; int n,k,a

哈夫曼树——贪心

哈夫曼树——贪心 哈夫曼树:给定n个权值作为n的叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman tree).哈夫曼树是带权路径长度                 最短的树,权值较大的结点离根较近. 此类题目一般求算哈夫曼树路径总值,利用贪心选择性质每次从队头取出两个边权和最小的树合并成新树入队(优先队列,保证队首元素最小),每次记录新树权值,当队内只剩一棵树时结束算法: priority_queue<long long,vector

最小优先队列实现赫夫曼树 贪心策略

使用 最小优先队列存放要编码的key,和合并之后内部节点,注意最小优先队列,获得最小值时会把最小是删掉,下面是java实现. package Algorithms; class MinQueue<T extends Comparable<? super T>>{ int heapSize; T[] heap; int capacity; public MinQueue(int capaticty) { this.capacity=capaticty; heapSize=0; //因

哈夫曼树/贪心/POJ 3253 Fence Repair

1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 using namespace std; 6 int n; 7 int a[20010]; 8 bool cmp(int x,int y) 9 { 10 return x>y; 11 } 12 int main() 13 { 14 int n; 15 scanf("%d&quo

UOJ#130 【NOI2015】荷马史诗 K叉哈夫曼树

[NOI2015]荷马史诗 链接:http://uoj.ac/problem/130 因为不能有前缀关系,所以单词均为叶子节点,就是K叉哈夫曼树.第一问直接求解,第二问即第二关键字为树的高度. #include< cstdio > #include< algorithm > typedef unsigned long long ull; template inline void read(T&x) { x=0;bool f=0;char c=getchar(); while

4198: [Noi2015]荷马史诗 (哈夫曼树基础)

一.题目概述 4198: [Noi2015]荷马史诗 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 1545  Solved: 818[Submit][Status][Discuss] Description 追逐影子的人,自己就是影子. --荷马 Allison 最近迷上了文学.她喜欢在一个慵懒的午后,细细地品上一杯卡布奇诺,静静地阅读她爱不释手的<荷马史诗>.但是由<奥德赛>和<伊利亚特>组成的鸿篇巨制<荷马史

NOI2015 荷马史诗 【k-哈夫曼树】

题目 追逐影子的人,自己就是影子 --荷马 Allison 最近迷上了文学.她喜欢在一个慵懒的午后,细细地品上一杯卡布奇诺,静静地阅读她爱不释手的<荷马史诗>.但是由<奥德赛>和<伊利亚特> 组成的鸿篇巨制<荷马史诗>实在是太长了,Allison 想通过一种编码方式使得它变得短一些. 一部<荷马史诗>中有n种不同的单词,从1到n进行编号.其中第i种单 词出现的总次数为wi.Allison 想要用k进制串si来替换第i种单词,使得其满足如下要求: