https://www.luogu.org/problemnew/show/P5119
题意:n头牛,每头牛有一个到达时间,每辆车最多可以接c头牛,总共有m辆车,问怎么安排,使得最大等待时间最小。
思路:最小化最大值,二分。
二分最长的等待时间,这个最大时间越短,越不容易满足条件,越长,越容易满足条件(需要的车辆数<=m),求的是最小的这个最大时间。
check()函数就是当当前车拉了超过c头牛或者当前牛距离该车开头已超过最大时间时新开辟一辆车。
与这道题https://blog.csdn.net/Wen_Yongqi/article/details/86774245不同,那道题求的是总的xxx,最优值未必每一块都有一个固定的xxx,可能一块儿大,一块儿小,总体最优,二分最小的最大值是这样:只要有那个最大值,不管别的怎么小,总体优不优看的是最大值,因此可以二分,而这道题不一样,总体优不优并不是取决于那个最大的,而是看 整体之和,如果强行二分,在该小时大了,就找不到最优了。最后是用贪心做了。
而本题要的是最小的最大值,也就是每一块儿的最大值都小于预设的的最大值,用二分求解。
也与这道题https://blog.csdn.net/Wen_Yongqi/article/details/87547500不同,那道题没有规定最多可以分多少组,只是规定了每组的最大容量,同样要的是总体最优,每一块儿都不固定怎么怎么样,同样,总体优不优并不取决于那个最大的,而是整体之和,这是不能够二分的。最后用dp做了。
这道题区别于那两题的地方就在于,总体优不优取决于每一段中最大的那个段,这就是二分适用“最小化最大值”的原因。
PS:老感觉https://blog.csdn.net/Wen_Yongqi/article/details/87547500和这道题很像,现在两道题互相融合一下。
①:这道题删掉最多m辆车这个条件,改为不限制车的数量,那就太简单了,直接每头牛派一辆车,最大值就是0。与dp无关。
②:那道题加一个条件:限制最多可以选m个区间,那么dp加一维,设f(i,j):前i个数选了j个区间的最优值,转移时f(i,j)<-max{f(i-x,j-1)+[i-x+1~i]},x∈[1,k],与二分无关。
由此可见,能不能用二分关键就在于:是不是求最大/小的最小/大值。是这个一般用二分,不是求这个一般不能用二分。
#include<bits/stdc++.h>
using namespace std;
#define maxn 100000+1000
int n,m,c,a[maxn],ans;
bool check(int l)
{
int ret=1,last=1; //当前正在处理第ret辆车
for(int i=1;i<=n;i++)
{
if(i-last>=c||a[i]-a[last]>l)
{
ret++;
last=i;
}
}
return ret<=m;
}
int main()
{
// freopen("input.in","r",stdin);
cin>>n>>m>>c;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
sort(a+1,a+1+n);
int l=1,r=a[n]-a[1],mid;
while(l<=r)
{
mid=(l+r)/2;
if(check(mid))ans=mid,r=mid-1;
else l=mid+1;
}
cout<<ans<<endl;
return 0;
}