文章目录
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5884
新学到的:
①判断是不是满的哈夫曼树:
为什么要判断是不是满的哈夫曼树喃?
因为满的哈夫曼树才是最优的
比如1 2 3 4,最多合并3个,并取最小的三个数1 2 3 就合并成6,然后再合并6 4
而是先合并1 2,成3,再合并3 3 4这样代价最小
k叉哈夫曼树:<mark>多余的节点=(N-1)%(k-1)</mark>
如果多余的节点计算出来是0,那就是满的
怎么理解喃?
看这个图,3叉哈夫曼树,每个只有最后一个是3个,而其他每层都只有(3-1)个,所以这个就很容易明白了
所以如果给的不是满的哈夫曼树,就阔以有两种方法变成满的:
①:把(多余的+1)个合并成1个
②:缺少的用0来补充
而我的代码是选择补充0,因为要方便补充0,因此才从大到小倒着来的
②不用优先队列
这道题因为有个二分就已经有个log了,所以用优先队列再来个log就有点卡时间了,因此这个优化在这道题中体现得淋漓尽致,我是先做的hdu1053练习的,我写得理解
#include"bits/stdc++.h"
using namespace std;
typedef long long LL;
const int maxn=2e5+5;
const int MOD=1e9+7;
LL a[maxn];
LL N,SUM;
queue<LL>que;
LL f(int k)
{
LL res=0;
while(!que.empty())que.pop();
int now=N;
if((N-1)%(k-1)!=0)
{
int tp=(N-1)%(k-1);//表示多余的
now+=(k-(tp+1));//加0的个数
}
while(now+que.size()>=k)
{
LL tp=0;
for(int i=1;i<=k;i++)
{
if(que.empty() || a[now]<=que.front())tp+=a[now--];//就是这里我now变成0了,但是我a[0]=0是个很小的数,所以就进去了,因此把a[0]=inf就行了
else if(now<1 || que.front()<=a[now])
{
tp+=que.front();
que.pop();
}
}
que.push(tp);
res+=tp;
}
return res;
}
int main()
{
int T;
cin>>T;
while(T--)
{
memset(a,0,sizeof a);
a[0]=1e9;//原来是now变成0的时候,a[0]=0最小,所以把他赋值成无穷大
cin>>N>>SUM;
for(int i=1;i<=N;i++)scanf("%lld",a+i);
sort(a+1,a+1+N,greater<int>());//方便加0的个数,所以倒起来
LL L=2,R=N,mid;
while(L<=R)
{
mid=L+R>>1;
LL tp=f(mid);
if(tp>SUM)L=mid+1;
else R=mid-1;
}
LL ans=min(R+10,N);
while(f(ans-1)<=SUM)ans--;//二分没学好,所以二分出来的答案再来枚举
printf("%lld\n",ans);
}
}