果果最近新学习了归并排序,对于归并排序时有两个序列ai和aj,果果将他们合并所需要的花费为ai+aj ,即两个序列⻓度之和。果果现在能⼀次最多合并k个序列,其花费为k个序列的⻓度之 和。但是教练为了不让果果浪费资源,让他进⾏完归并排序后的花费不超过C。果果为了偷懒想每次合 并的序列数尽可能少,于是果果想请你告诉他,他能完成归并排序且总的花费⼩于C最⼩的k是多少 呢。
输入格式
第⼀⾏有两个整数 n ,C表示有n个序列,总的花费不超过C。 第⼆⾏有n个整数ai,表示要合并的n个序列,每个序列的⻓度。
输出格式
输出为⼀⾏,表示最⼩的k。
样例
样例输入
5 20
1 2 3 4 5
样例输出
4
样例解释
先合并(1,2)为3 ,再合并(3,3,4,5) 为 15。总花费为18⼩于20。
题解
题解
首先,如果合并次数越多,则花费就越多;反之,如果合并次数越少,则花费就越少,满足二分的条件,而合并次数由每次合并的个数决定,所以只需要二分每次合并的个数即可。
但是,设每次合并k个,我们不可以保证最后一次k个合并后只剩下一个数,所以不可以保证最优。
对于每次合并,我们都会留下一个数,所以每次可以丢掉k-1个数,最后我们希望丢掉n-1个数,所以一开始我们就需要合并(n-1)%(k-1)+1个数,这样就可以保证最后一次操作后只剩下一个数,又因为我们需要让值最小,所以一开始合并的数值要小,所以先排序。
代码
#include<bits/stdc++.h>
using namespace std;
int a[100005];
int n,m;
bool Cheak(int x){
priority_queue<int,vector<int>,greater<int> >q;
int tot=0;
int ans=0;
int now=(n-1)%(x-1);
if(now>0){
for(int i=1;i<=now+1;i++){
tot+=a[i];
}
ans+=tot;
q.push(tot);
tot=0;
}
for(int i=now+2;i<=n;i++){
q.push(a[i]);
}
while(!q.empty()){
tot=0;
for(int i=1;i<=x;i++){
if(!q.empty()){
tot+=q.top();
q.pop();
}
else
break;
}
ans+=tot;
if(!q.empty()){
q.push(tot);
}
}
if(ans>m)
return 0;
return 1;
}
int main(){
scanf("%d%d",&n,&m);
//cerr<<n<<' '<<m<<endl;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
//cerr<<a[i]<<' ';
}
sort(a+1,a+n+1);
int l=2,r=n;
int ans=0;
while(l<=r){
int mid=(l+r)>>1;
if(Cheak(mid)){
r=mid-1;
ans=mid;
}
else{
l=mid+1;
}
}
printf("%d",ans);
return 0;
}
/*
10 75
6 6 10 4 2 7 9 9 6 10
*/