1. 程式人生 > >貪心+哈夫曼樹--bzoj4198[NOI2015]荷馬史詩

貪心+哈夫曼樹--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); }