zoukankan      html  css  js  c++  java
  • [NOI2015]荷马史诗

    题面:

    追逐影子的人,自己就是影子 ——荷马

    Allison 最近迷上了文学。她喜欢在一个慵懒的午后,细细地品上一杯卡布奇诺,静静地阅读她爱不释手的《荷马史诗》。但是由《奥德赛》和《伊利亚特》 组成的鸿篇巨制《荷马史诗》实在是太长了,Allison 想通过一种编码方式使得它变得短一些。

    一部《荷马史诗》中有n种不同的单词,从1到n进行编号。其中第i种单 词出现的总次数为wi。Allison 想要用k进制串si来替换第i种单词,使得其满足如下要求:

    对于任意的 1 ≤ i, j ≤ n , i ≠ j ,都有:si不是sj的前缀。

    现在 Allison 想要知道,如何选择si,才能使替换以后得到的新的《荷马史诗》长度最小。在确保总长度最小的情况下,Allison 还想知道最长的si的最短长度是多少?

    一个字符串被称为k进制字符串,当且仅当它的每个字符是 0 到 k − 1 之间(包括 0 和 k − 1 )的整数。

    字符串 str1 被称为字符串 str2 的前缀,当且仅当:存在 1 ≤ t ≤ m ,使得str1 = str2[1..t]。其中,m是字符串str2的长度,str2[1..t] 表示str2的前t个字符组成的字符串。

    看数据范围及样例

    题解:

    如果学过huffman tree这题就不难了!

    当然只知道huffman tree还是不行的,还要知道哈夫曼编码,

    哈夫曼树貌似一开始就是用来获取哈夫曼编码的?

    这个网上讲解很多,就不在多讲。

    此题中如果按照出现次数为权值来构建哈夫曼树,则总长度一定最小,

    原版哈夫曼树是2进制的,因此只能放2叉,

    但这里是k进制,所以就可以放k叉了,

    所以每次用优先队列找出前k小然后合并即可

    但是要注意如果k != 2的话,就要保证每次合并都有k个节点(直到最后合并成一个),

    所以要在最开始补一些空节点进去,

    因为每次都是拿出k个,放入一个,所以实际上是消掉了(k-1)个,

    然后最后需要剩下一个,因此我们需要n % (k-1) == 1,

    然后补齐需要的即可

    那为什么用出现次数作为权值就最优呢?

    因为出现次数越大的单词越浅,所以编码长度也越小,自然总长度就最小了

    但是这里还需要注意一个东西,就是第二问的要求,

    需要最长的编码最短,依旧是用贪心的思想,

    在权值相同的情况下,每次优先合并被合并次数少的节点(最深的节点更浅)

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define R register int
      4 #define    AC 100100
      5 #define ac 500100
      6 #define getchar() *o++
      7 #define LL long long 
      8 char READ[5000100],*o=READ;
      9 int n,k,id,deep;
     10 int d[ac];//w为点权(包括新增点)
     11 int Head[ac],Next[ac],date[ac],tot;
     12 LL ans;
     13 LL w[ac];//error!!!权值很大的啊
     14 bool done;
     15 struct cmp1{
     16     bool operator () (int &a,int &b)
     17     {
     18         if(w[a] != w[b])    return w[a] > w[b];
     19         else return d[a] > d[b];
     20     }
     21 };
     22 
     23 priority_queue<int,vector<int>,cmp1>q;
     24 /*哈弗曼编码,这样是最短的,但无需获取具体的编码,
     25 只要获得每个单词对应的出现次数(题目给定)和编码长度(哈弗曼树深度)即可,
     26 因为是k进制,所以建立k叉哈弗曼树,最后dfs一遍获取每个叶节点的深度,
     27 然后对出现次数与深度的积求和即可*/
     28 inline LL read()
     29 {
     30     LL x=0;char c=getchar();
     31     while(c > '9' || c < '0') c=getchar();
     32     while(c >= '0' && c <= '9') x=x*10+c-'0',c=getchar();
     33     return x;
     34 }
     35 
     36 inline void add(int f,int w)
     37 {
     38     date[++tot]=w,Next[tot]=Head[f],Head[f]=tot;
     39 }
     40 
     41 void dfs(int x,int dep)
     42 {
     43     int now;
     44     if(!Head[x])
     45     {
     46         ans+=(LL)dep * w[x];
     47         if(w[x]) deep=max(deep,dep);
     48         return ;    
     49     }
     50     for(R i=Head[x]; i ;i=Next[i])
     51     {
     52         now=date[i];
     53         dfs(now,dep+1);
     54     }
     55 }
     56 
     57 bool build()
     58 {
     59     if(done)
     60         if(q.empty() || q.size() == 1) return false;
     61     int x=++id,now;//动态加点
     62     for(R i=1;i<=k;i++)
     63     {
     64         if(q.empty()) break;
     65         now=q.top();
     66         q.pop();
     67         w[x] += w[now];
     68         d[x] = max(d[x] , d[now]);
     69         add(x,now);//建树
     70     }
     71     ++d[x];//这次合并也要算啊
     72     q.push(x);
     73     done=true;
     74     return true;
     75 }
     76 
     77 void pre()
     78 {
     79     n=read(),k=read();
     80     id=n;
     81     for(int i=1;i<=n;i++) 
     82     {
     83         w[i]=read();
     84         q.push(i);
     85     }
     86     if(k != 2)//但是只有不为二叉的时候才要考虑这些
     87     {
     88         int now=0;
     89         if(!(n % (k-1)))//当余数为0的时候无需再跨越一次,,,, 
     90         {
     91             q.push(++id);
     92         } 
     93         else
     94         if(n % (k-1) != 1) //huffman要求完美合并?
     95         {
     96             now += (k-1) - (n % (k-1)) + 1;
     97             for(R i=1;i<=now;i++) q.push(++id);//error!!!是权值为0,不是编号为0!!!
     98         }
     99     }
    100 }
    101 
    102 void work()
    103 {
    104     while(build());
    105     dfs(id,0);
    106     printf("%lld
    ",ans);
    107     printf("%d
    ",deep);
    108 }
    109 
    110 int main()
    111 {
    112 //    freopen("in.in","r",stdin);
    113     fread(READ,1,5000000,stdin);
    114     pre();
    115     work();
    116 //    fclose(stdin);
    117     return 0;
    118 }
  • 相关阅读:
    Java 图片处理——如何生成高清晰度而占有磁盘小的缩略图
    Java文件IO操作应该抛弃File拥抱Paths和Files
    什么是内存(一):存储器层次结构
    mysql 锁
    Mysql索引面试题
    node.js中对同步,异步,阻塞与非阻塞的理解
    mysql 快照读 当前度
    mysql 联合索引
    redis 分布式锁(单机)
    docker 环境搭建redis集群相关问题
  • 原文地址:https://www.cnblogs.com/ww3113306/p/9143281.html
Copyright © 2011-2022 走看看