题意:
n个有序序列的归并排序。每次可以选择不超过k个序列进行合并,合并代价为这些序列的长度和.总的合并代价不能超过T
, 问k
最小是多少
思路:
二分k,判断是否成立。
每次要合并k个,但未必会恰好每次都合并k个。所以呢为了代价最小,先合并多余的那几个最小的。
(n-1)%(k-1)== 0 是恰好合并k次的判断条件,为什么呢?因为合并k个,相当于从n中减去了k-1,n最终应该多出一个,这个靠自己想。
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
typedef long long ll;
ll a[100000+10];
int t,T,n;
queue<int> q1,q2;
bool judge(int k)
{
while(!q1.empty())q1.pop();
while(!q2.empty())q2.pop();
for(int i =1;i<=n;i++) q1.push(a[i]);
ll sum = 0,ans = 0;//sum每次merge的花费,ans总花费
if((n-1)%(k-1)!=0) // 为使花费最少,先将多余的加起来
{
int num = (n-1)%(k-1)+1;//重点!+1是因为合并后还会剩一个
for(int i=1;i<=num;i++)
{
sum+=q1.front();
q1.pop();
}
q2.push(sum);
ans+=sum;
}
while(!q1.empty())
{
sum = 0;
for(int i=1;i<=k;i++)
{
if(!q1.empty()&&!q2.empty())
{
if(q1.front()<=q2.front())
{
sum+=q1.front();
q1.pop();
}
else
{
sum+=q2.front();
q2.pop();
}
}
else if(!q1.empty())
{
sum+=q1.front();
q1.pop();
}
else if(!q2.empty())
{
sum+=q2.front();
q2.pop();
}
}
ans+=sum;
q2.push(sum);
}
if(ans>T)
return false;
sum=0;
int num=0;
while(!q2.empty())
{
if(q1.size()==1)
break;
sum+=q2.front();
q2.pop();
num++;
if(num==k)
{
q2.push(sum);
ans+=sum;
sum = 0;
num = 0;
if(q2.size()==1)
break;
}
}
if(ans>T) return 0;
return 1;
}
int main()
{
ios::sync_with_stdio(false);
cin>>t;
while(t--)
{
cin>>n>>T;
for(int i=1;i<=n;i++)
cin>>a[i];
sort(a+1,a+n+1);
int l=2,r=n,mid;
int ans = 1;
while(l<=r)
{
mid=(l+r)/2;
if(judge(mid))
{
ans=mid;
r=mid-1;
}
else
l=mid+1;
}
cout<<ans<<endl;
}
return 0;
}