定义:
哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。
K叉哈夫曼树:
哈夫曼树的构造方法是贪心的每次选最小的几个节点构造。
当的时候需要预处理一下,因为可能最后一步合并操作的点数不到K个节点,这样的话就不是最优的了。
预处理方法:
当时:
- 加入个权重为0的虚拟节点。
- 将个节点先合并为1个节点。(有可能只有个节点)
构造哈夫曼树:
每次取最小的个数合并为一个节点。
这里很容易想到用优先队列去写,复杂度。
如果数组是有序的,我们可以用两个队列去优化一下,将原先的节点放入,合并之后的节点放入,这样仍然满足单调性,只需要每次取数的时候取两个队列中小的那个即可,复杂度
例题:NC7528D 合并序列
Code:
#include<bits/stdc++.h> #define fi first #define se second #define mp make_pair using namespace std; typedef long long ll; typedef pair<int,int>P; const double eps = 1e-8; const int NINF = 0xc0c0c0c0; const int INF = 0x3f3f3f3f; const ll mod = 1e9 + 7; const ll N = 1e5 + 5; ll n,m,a[N]; bool check(int k){ queue<ll> q1,q2; if((n-1)%(k-1)) for(int i=1;i<=k-1-(n-1)%(k-1);i++) q1.push(0); for(int i=1;i<=n;i++) q1.push(a[i]); ll sum=0; while(true){ ll tmp=0; for(int i=1;i<=k;i++){ if(q1.empty() && q2.empty()) break; int a1,a2; if(q1.empty()){ tmp+=q2.front(); q2.pop(); } else if(q2.empty()){ tmp+=q1.front(); q1.pop(); } else{ a1=q1.front(); a2=q2.front(); if(a1<a2) {tmp+=a1;q1.pop();} else {tmp+=a2;q2.pop();} } } sum+=tmp; if(q1.empty() && q2.empty()) break; q2.push(tmp); } return sum<=m; } int main(){ ios::sync_with_stdio(false); cin.tie(0); cin>>n>>m; for(int i=1;i<=n;i++) cin>>a[i]; sort(a+1,a+1+n); ll L=2,R=n+1,mid,ans=n; while(L<R){ mid=(L+R)/2; if(check(mid)) R=mid,ans=min(ans,mid); else L=mid+1; } cout<<ans<<'\n'; return 0; }
Code2
#include <bits/stdc++.h> #define fi first #define se second #define mp make_pair using namespace std; typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int NINF = 0xc0c0c0c0; const int INF = 0x3f3f3f3f; const ll mod = 1e9 + 7; const ll N = 1e6 + 5; int n,m,a[N]; inline bool check(int k){ queue<ll> q1,q2; ll sum=0; int rem=(n-1)%(k-1); if(rem){ for(int i=1;i<=min(rem+1,n);i++) sum+=a[i]; if(n==rem) return sum<=m; q2.push(sum); for(int i=rem+2;i<=n;i++) q1.push(a[i]); }else{ for(int i=1;i<=n;i++) q1.push(a[i]); } while(true){ ll tmp=0; for(int i=1;i<=k;i++){ if(q1.empty() && q2.empty()) break; if(q1.empty()){ tmp+=q2.front(); q2.pop(); } else if(q2.empty()){ tmp+=q1.front(); q1.pop(); } else{ if(q1.front()>q2.front()){ tmp+=q2.front(); q2.pop(); }else{ tmp+=q1.front(); q1.pop(); } } } sum+=tmp; if(q1.empty() && q2.empty()) break; q2.push(tmp); } return sum<=m; } int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); cin>>n>>m; for(int i=1;i<=n;i++) cin>>a[i]; sort(a+1,a+1+n); int L=2,R=n+1,mid; while(L<R){ mid=(L+R)/2; if(check(mid)) R=mid; else L=mid+1; } cout<<L<<'\n'; return 0; }