hdu5884 Sort(二分+不完美k叉哈夫曼树求值+利用单调性进行队列优化)

题意:

在这里插入图片描述

解法:
显然二分.

二分答案mid,check其实就是计算mid叉哈夫曼树的权值.
可以用优先队列做,每次合并减少mid-1和节点,合并完只剩下一个节点.

但是如果(n-1)%(mid-1)!=0,
那么说明该哈夫曼树的下面存在一个节点,它的子节点不满k叉,
设rem=(n-1)%(mid-1),说明多出了rem的节点,
我们要优先消掉这rem个节点,因此一开始需要先合并rem+1个节点,
之后哈夫曼树就是完全二叉树了,直接模拟即可.

算法复杂度O(n*log*log).

提交代码之后发现会超时,需要优化.

考虑到合并的过程中,合并出的节点的权值是单调递增的,
因此我们可以开两个队列,一个队列q用来存为被合并过的节点,
另一个队列qq用来存合并出来的节点,
我们预先对a[]排序,那么队列q一开始是有序的,
同时由于合并出的节点权值是递增的,合并出的节点都丢进队列qq,
那么去队列qq也是有序的.

每次取数,我们只需要比较两个队列头部的数,取较小的即可.

这样check部分就是O(n)的了,
算法总复杂度O(n*log),可以通过此题.
code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxm=1e6+5;
int a[maxm];
int n,t;
bool check(int mid){
    queue<int>q,qq;
    for(int i=1;i<=n;i++){
        q.push(a[i]);
    }
    int ans=0;
    int rem=(n-1)%(mid-1);
    if(rem){//如果不能整除.
        int sum=0;
        for(int i=1;i<=rem+1;i++){//要取rem+1个,因为需要减少rem个节点.
            sum+=q.front();q.pop();
        }
        ans+=sum;
        if(ans>t)return 0;
        qq.push(sum);
    }
    while(q.size()+qq.size()>1){
        int sum=0;
        for(int i=1;i<=mid&&(q.size()||qq.size());i++){
            if(q.size()&&qq.size()){//两边都有,取较小值.
                if(q.front()<=qq.front()){
                    sum+=q.front();q.pop();
                }else{
                    sum+=qq.front();qq.pop();
                }
             }else if(q.size()){
                sum+=q.front();q.pop();
             }else{
                sum+=qq.front();qq.pop();
            }
        }
        ans+=sum;
        if(ans>t)return 0;
        qq.push(sum);
    }
    return 1;
}
void solve(){
    cin>>n>>t;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    sort(a+1,a+1+n);
    int l=2,r=n;
    int ans=n;
    while(l<=r){
        int mid=(l+r)/2;
        if(check(mid))ans=mid,r=mid-1;
        else l=mid+1;
    }
    cout<<ans<<endl;
}
signed main(){
    ios::sync_with_stdio(0);
    int T;cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值