hdu #5884 Sort(快速哈夫曼树+队列)

标签:快速哈夫曼树,队列
原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=5884
这道题与上一道题十分相似,本质上都是快速哈夫曼树算法,只不过比上一道题稍难一些。
Recently, Bob has just learnt a naive sorting algorithm: merge sort. Now, Bob receives a task from Alice.
Alice will give Bob N sorted sequences, and the i-th sequence includes ai elements. Bob need to merge all of these sequences. He can write a program, which can merge no more than k sequences in one time. The cost of a merging operation is the sum of the length of these sequences. Unfortunately, Alice allows this program to use no more than T cost. So Bob wants to know the smallest k to make the program complete in time.
Input
The first line of input contains an integer t0, the number of test cases. t0 test cases follow.
For each test case, the first line consists two integers N (2N100000) and T (Ni=1ai<T<231) .
In the next line there are N integers a1,a2,a3,...,aN(i,0ai1000).
Output
For each test cases, output the smallest k .
题意大概就是,把一个数列以每次最多合并k个数的方式合并起来,直至剩下一个数为止,合并的代价是被合并的数的和,求最小的k使总代价w满足 wt
首先很明显,合并数的策略就是选最小的数进行合并,所以我们可以采用与上一题相似的算法,不过只需要开两个队列。设pd(x)表示k取到x时,合并的总代价,那么就有性质 pd(a)pd(b) 当且仅当 ab ,所以这道题可以二分。因为求 pd(x) 代价是 O(n) ,所以总时间复杂度是 O(nlogn)
具体过程见代码

#include<cstdio>
#include<algorithm>
#include<queue>
#define maxn 100050
typedef long long ll;
using namespace std;
int t0,n,k,t,a[maxn],l,r,ans,mid,sum[maxn];
queue <int> qu1,qu2;
bool pd(int k){  
    while(!qu1.empty()) qu1.pop();  
    while(!qu2.empty()) qu2.pop();  
    for(int i=1;i<=n;i++) qu1.push(a[i]);  
    int num=0;  
    ll sum=0,ans=0;  
    if((n-1)%(k-1)!=0){  
        num=(n-1)%(k-1)+1;  
        for(int i=1;i<=num;i++) {  
            sum+=qu1.front();  
            qu1.pop();  
        }  
        qu2.push(sum);  
        ans+=sum;  
    }  
    while(!qu1.empty()){  
        sum=0;  
        for(int i=1;i<=k;i++){  
            if(!qu1.empty() && !qu2.empty() ){  
                if(qu1.front()<=qu2.front()){  
                    sum+=qu1.front();  
                    qu1.pop();  
                } else {  
                    sum+=qu2.front();  
                    qu2.pop();  
                }  
            } else if(qu1.empty()){  
                sum+=qu2.front();  
                qu2.pop();  
            } else if(qu2.empty()){  
                sum+=qu1.front();  
                qu1.pop();  
            }  
        }  
        ans+=sum;  
        qu2.push(sum);  
    }  
    if(ans>t) return false;  
    sum=0;num=0;  
    while(!qu2.empty()){  
        sum+=qu2.front();  
        qu2.pop();  
        num++;  
        if(num==k){  
            qu2.push(sum);  
            ans+=sum;  
            sum=0;  
            num=0;  
            if(qu2.size()==1) break;  
        }  
    }  
    if(ans>t) return false;  
    return true;  
}  
int main()
{
    int i,j;
    scanf("%d",&t0);
    while (t0--)
    {
        scanf("%d%d",&n,&t);
        for (i=1;i<=n;i++) scanf("%d",&a[i]); 
        sort(a+1,a+n+1); 
        l=2; r=n; ans=1; sum[0]=0;
        for (i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
        while (l<=r)
        {
            mid=(l+r)/2;
            if (pd(mid))
            {
                r=mid-1; ans=mid;
            }
            else l=mid+1;
        }
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值