题目传送门
关于翻译:
很显然,翻译中的“喝第杯咖啡可完成
项作业”应为“喝第
杯咖啡可完成
项作业”。
思路:
我们读题可以发现,每一次喝咖啡,完成的作业数就会减一,而最少只能减到0,所以,假设我们在第114514杯时喝值为1的咖啡,这样只会损失1份作业,而如果喝值为114514的咖啡,那么将会损失114514份作业,很显然,第一种方案是更划算的。
所以,我们得出结论:为了将损失降到最少,我们需要将值大的咖啡放在前面喝,将值小的咖啡放在后面喝。
具体实现:
我们来看样例一。
先假设只需要一天。
喝的咖啡的值 | 3 | 2 | 2 | 1 | 1 |
完成的作业数 | 3 | 3+(2-1)=4 | 4+(2-2)=4 | 4+max(0,1-3)=4 | 4+max(0,1-4)=4 |
很显然,这并没有完成作业。
再假设需要二天。
第一天 | 3 | 2 | 1 |
第二天 | 2 | 1 | |
完成的作业数 | 3+2=5 | 5+(2-1)+(1-1)=6 | 6+max(0,1-2)=6 |
这也没有完成作业。
然后假设需要三天。
第一天 | 3 | 1 |
第二天 | 2 | 1 |
第三天 | 2 | / |
完成的作业数 | 3+2+2=7 | 7+(1-1)+(1-1)=7 |
这差一点就完成了作业,但依然没有完成作业。
最后假设需要四天。
第一天 | 3 | 1 |
第二天 | 2 | / |
第三天 | 2 | / |
第四天 | 1 | / |
完成的作业数 | 3+2+2+1=8 | 8+(1-1)=8 |
花了四天四夜,终于完成了作业。
代码流程:
1.将咖啡的值排序。
2.由于一个一个找需要几天会TLE,所以我们就需要用二分。
3.用check(x)函数进行模拟,x表示需要几天,我们需要定义两个变量,一个表示第几天,另一个表示第几杯咖啡,一个一个去扫,在用cnt加上完成的作业数,最后比较即可。
时间复杂度:
二分的时间复杂度为,check(x)的时间复杂度为
。
完整代码:
#include<iostream>
#include<algorithm>
using namespace std;
long long n,m,a[201000];
bool cmp(long long c,long long b){
return c>b;
}bool check(long long mid){
long long cnt=0;
long long k=1,j=1;
for(long long i=1;i<=n;i++){
cnt+=max((long long)0,a[i]-j+1);
k++;
if(k>mid){
k=1;
j++;
}
}//cerr<<endl;
if(cnt<m)return false;
return true;
}
int main(){
cin>>n>>m;
long long he=0;
for(long long i=1;i<=n;i++){
cin>>a[i];
he+=a[i];
}if(he<m){
cout<<-1;
return 0;
}
sort(a+1,a+1+n,cmp);
long long l=1,r=n,mid;
while(l<=r){
mid=(l+r)/2;
if(check(mid)){
r=mid-1;
}else{
l=mid+1;
}
}cout<<l;
return 0;
}