Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 26420 | Accepted: 11341 |
Description
Input
Output
Sample Input
dog ogday cat atcay pig igpay froot ootfray loops oopslay atcay ittenkay oopslay
Sample Output
cat eh loops
Hint
Source
农夫约翰新建了个大牧场,农场中有N (2 <= N <= 100,000)个牛房,每个牛房都坐落在同一条线上,其坐标分别是x1,...,xN (0 <= xi <= 1,000,000,000).
农夫约翰有C (2 <= C <= N)头牛,但这些却并不喜欢这些牛房的布局。因此,这些牛就会冲到其他牛房去。为了防止牛牛们互相冲撞而受伤,农夫约翰就想好好安置这些牛牛们,使它们两两之间的最小距离最大。那么这个距离到底应该是多少呢?
输入:
第一行:两个数N和C
接下来N行,每行包含一个整数xi代表第i个牛房的坐标
输出:
输出一行,牛牛们两两相距的最大的最小距离
输入样例:
5 3
1
2
8
4
9
输出样例:
3
分析:
由题意再稍加推理不难知道这最大的距离必然是等于某两个牛房的距离。所以可以枚举任意两个牛房的距离作为最小距离d,看是否能分出c-1块不小于d的分区,再这些符和条件的d中选最大即可。0(n^2)的复杂度但由于题目数据范围比较大,这样时间不允许。因此需要优化。
由题意可知,这个答案d必定是小于等于相隔最远的两个牛房的距离差除以c-1假设为s,那么d的范围就是[0,s]。然后就可以用二分法在[0,s]中查找符合要求的d了。因为这个d是要取小于或等于分出的c-1块区域的最小值,因此,不能采用增1提高下界,与减1降低上界同时进行的方法,因为如果这样有可能搜不出答案。
while(left<right)
{
if(ok) //mid满足条件
{
left = mid+1; //增1提高下边界
}
else
{
right = mid - 1; //减1降低上边界
}
}
例如:left=1;right=9;mid=5;合理答案是5,所以这个mid满足条件,但又没有结束,只能提高下界left=mid+1=6;这样就导致搜不出正确的解。因此,这里需要采用,不增1提高下界,减1降低上界的策略。
while(left<right)
{
if(ok) //mid满足条件
{
left = mid; //不增1提高下边界
}
else
{
right = mid - 1; //减1降低上边界
}
}
这样就能保证,一定不会吧解搜没了。当这样问题又来了,这mid=(left+right)/2是应该向上取整呢?还是向下取整呢?假设是向下取整,那么这样二分搜索有可能进入死循环。
例如:left=2;right=3;mid=2;答案就是2因为不增1提高下界所以这会一直循环。因此,这里应该向上取整,而mid=(left+right)/2,这个式子中在计算机中默认取下整。因此为了让其取上整,mid=(left+right+1)/2;这样本题就很容易解决了。
二分代码:
#include<stdio.h>
#include<cstring>
#include<algorithm>
using namespace std;
const int mm=100010;
int f[mm];
int n,c;
int main()
{
while(scanf("%d%d",&n,&c)!=EOF)
{
for(int i=0;i<n;i++)
scanf("%d",&f[i]);
///距离排序
sort(f,f+n);
///c只牛,只需要分出c-1个距离区间
c--;
int left,right,mid,dex,num;
///令左边界为0,右边界为可能的最大值
left=0;
mid=right=(f[n-1]-f[0])/c;
while(left<right)
{ int i;
dex=0;num=0;
for(i=dex+1;i<n;i++)
{
if(f[i]-f[dex]>=mid)
{
///记录上一个区间边界
dex=i;
///记录已经分出的符合要求的区间数
num++;
if(num==c)
break;
}
}
///如果i==n说明未分出c个合法区间
if(i==n)
right=mid-1;
else
left=mid;
///向上取整
mid=(left+right+1)/2;
}
printf("%d\n",mid);
}
}