标签:快速哈夫曼树,队列
原题链接: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
Input
The first line of input contains an integer
For each test case, the first line consists two integers
N (2≤N≤100000)
and
T (∑Ni=1ai<T<231)
.
In the next line there are
N
integers
Output
For each test cases, output the smallest
k
.
题意大概就是,把一个数列以每次最多合并k个数的方式合并起来,直至剩下一个数为止,合并的代价是被合并的数的和,求最小的k使总代价
首先很明显,合并数的策略就是选最小的数进行合并,所以我们可以采用与上一题相似的算法,不过只需要开两个队列。设pd(x)表示k取到x时,合并的总代价,那么就有性质
pd(a)≤pd(b)
当且仅当
a≤b
,所以这道题可以二分。因为求
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;
}