1. 程式人生 > >【BZOJ 4198】[Noi2015]荷馬史詩 哈夫曼編碼

【BZOJ 4198】[Noi2015]荷馬史詩 哈夫曼編碼

clu tor space zoj col 具體實現 %d sca bool

合並果子加強版.......

哈夫曼樹是一種特別的貪心算法,它的作用是使若幹個點合並成一棵樹,每次合並新建一個節點連接兩個合並根並形成一個新的根,使葉子節點的權值乘上其到根的路徑長的和最短(等價於每次合並的代價是合並根的權值和,求最小代價)。實現過程就是每次合並權值最小的兩個節點,具體一下就是建個森林,每次取最小的兩個然後權值加和再放入,重復。

他的實際應用就是哈夫曼編碼,拓展就是k叉(本題),對於k叉也就是k進制,如果葉子節點不是1+(k-1)*x的形式,那麽就加權值為0的點使他變成此種形式,不能到最後一次再加,那樣做不是最優樹。

關於哈夫曼編碼有靜態(本題)和動態,並不會動態......

具體實現的話,工程裏是循環找最小,oi裏是優先隊列。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define HoN Heap::
#define make(a,b) ((Heap::V){(a),(b)})
typedef long long LL;
const int N=100010;
namespace Heap{
  struct V{
    LL val;int deep;
    inline friend bool operator <(V a,V b);
  }k[N];
  
int len; inline bool operator <(V a,V b){ return a.val<b.val||(a.val==b.val&&a.deep<b.deep); } inline bool empty(){return len==0;} inline V top(){return k[1];} inline int size(){return len;} inline void pop(){ k[1]=k[len--];register int now=1; while(now<=(len>>1
)){ int next=now<<1; if(next<len&&k[next|1]<k[next])next|=1; if(k[now]<k[next])return; std::swap(k[now],k[next]),now=next; } } inline void push(V key){ k[++len]=key;register int now=len; while(now!=1&&k[now]<k[now>>1]) std::swap(k[now],k[now>>1]),now>>=1; } } int n,k; LL ans; int main(){ scanf("%d%d",&n,&k);LL x; for(int i=1;i<=n;++i) scanf("%lld",&x),HoN push(make(x,1)); if(k!=2&&n%(k-1)!=1){ if(n%(k-1)==0)HoN push(make(0,1)),++n; else{ for(int i=1;i<=k-(n%(k-1));++i) HoN push(make(0,1)); n+=n%(k-1); } } int m=k==2?n-1:n/(k-1); int max;HoN V use; while(m--){ x=0,max=0; for(int i=1;i<=k;++i) use=HoN top(),HoN pop(),x+=use.val,max=std::max(max,use.deep); ans+=x,HoN push(make(x,max+1)); } printf("%lld\n%d",ans,HoN top().deep-1); return 0; }

【BZOJ 4198】[Noi2015]荷馬史詩 哈夫曼編碼