https://blog.csdn.net/a540524095/article/details/51984698
二分查找每个人最多看几次病后,时间结束
通过二分法来获得最后每个人最多看?(middle)次
即最后看完的那个人(lucky dog)的被看的次数
当num[i]<middle时,那么第i个一定已经看完病了。
lucky dog也不一定看完了病
①如果lucky dog看完病了,
那lucky dog之前num[i]=middle的病人和lucky dog,一样幸运,看完了病。
那lucky dog之后num[i]=middle的病人就没有lucky dog那么幸运。还差一次才能看完
num[i]>middle的病人是一定看不完病的。
②lucky dog 没有看完病
那么num[i]>=middle(包括lucky dog)是看不完病的。
轮一遍病人的序列,sum=middle*n
当num[i]<k时,这个人有(middle-num[i])次看病为假,middle*n减去middle和num[i]的差值
剩下的是sum,超过k的部分为假。
也有可能刚刚等于K,此时看病次数和lucky dog相同的病人全部看完
sum>=k;
#include<stdio.h>
#define maxn 100000
long long patient[maxn+10];
long long answer[maxn+10];
int main()
{
long long n,k,sum=0,maxi=0;
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%lld",&patient[i]);
sum+=patient[i];
if(maxi<patient[i])
maxi=patient[i];
}
if(sum<k)
{
printf("-1\n");
return 0;
}
else if(sum==k)
{
return 0;
}
long long left,right,middle,times,where,ans;
left=0;
right=maxi;
while(left<=right)
{
middle=(left+right)/2;
//printf("\nleft=%lld right=%lld middle=%lld\n",left,right,middle);
times=middle*n;
for(long long i=1;i<=n;i++)
if(patient[i]<middle)
times-=(middle-patient[i]);
//printf("times=%lld k=%lld\n",times,k);
if(times>=k)
{
ans=middle;
right=middle-1;
}
else if(times<k)
{
left=middle+1;
}
}
//printf("********ans=%lld\n",ans);
for(long long i=1;i<=n;i++)
{
if(ans>patient[i])
{
k-=patient[i];
patient[i]=0;
}
else
{
k-=(ans-1);
patient[i]-=ans-1;
}
}
int i=1;
while(k>0)
{
if(patient[i]>0)
{
patient[i]--;
k--;
}
if(k==0)
where=i;
i++;
}
int cnt=0;
for(long long j=where+1;j<=n;j++)
{
if(patient[j]>0)
answer[cnt++]=j;
}
for(long long j=1;j<=where;j++)
{
if(patient[j]>0)
{
answer[cnt++]=j;
//printf("answer[%d]=%lld\n",cnt-1,answer[cnt-1]);
}
}
for(long long j=0;j<cnt;j++)
if(j!=cnt-1)
printf("%lld ",answer[j]);
else
printf("%lld\n",answer[j]);
return 0;
}