贪心+哈夫曼树--bzoj4198[NOI2015]荷马史诗

传送门

看到这个题一脸懵逼啊?难道是字符串??
结果一看题解,是什么哈夫曼树(不会啊)

这里留个坑···还没有十分了解哈夫曼树是啥
但是看样子好像就是一棵二叉树,每次将权值小的合并成新的点
新点权值是原来的和
这个背景好像很裸???

Huffman树之所以可以保证合法性,是因为加入了许多虚拟结点,保证了一定不会有一个是另一个前缀的情况,考虑一个结点到根结点的路径上的所有点一定都是虚拟结点,所以很容易确定构造方法的合法。

求第二问的时候我们可以合并的时候优先合并深度较小的
这样就能保证整个树的最大深度最小了(为什么yy一下!)

当k≠2时,完美合并(感性理解这个词)需要满足n≡1 (mod k−1),不满足这个条件时相当于额外添了几个出现次数为0的单词
所以k≠2时我们先补权值为0的节点使得可以完美合并,之后贪心即可

#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
#define maxn 100005
#define LL long long
using namespace std;
int n,k,ans2;
LL ans1;

inline LL rd(){
    LL x=0,f=1;char c=' ';
    while(c<'0' || c>'9') {if(c=='-')f=-1;c=getchar();}
    while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
    return x*f;
} 

struct node{
    LL val; int dep;
    inline bool operator <(const node &x) const {
        return val>x.val||(val==x.val&&dep>x.dep);
    }
}tmp;
priority_queue<node> q;

int main(){
    scanf("%d%d",&n,&k); LL x;
    for(int i=1;i<=n;i++){
        x=rd(); q.push((node){x,1});
    }
    int rm=(n-1)%(k-1);
    if(rm) rm=k-1-rm,n+=rm;
    for(int i=1;i<=rm;i++) q.push((node){0,1});//用0来补齐
    while(n>1){
        int maxd=0; LL tot=0;
        for(int i=1;i<=k;i++){
            tmp=q.top(); q.pop();
            tot+=tmp.val;
            maxd=max(maxd,tmp.dep); 
        }
        q.push((node){tot,maxd+1});//从下往上建树 
        ans1+=tot; ans2=max(ans2,maxd);
        n-=k-1;
    }
    printf("%lld\n%d\n",ans1,ans2);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值