原题 传送门
【题目描述】
农夫 John 建造了一座很长的畜栏,它包括N (2 ≤ N ≤ 100,000)个隔间,这些小隔间依次编号为x1,...,xN (0 ≤ xi ≤ 1,000,000,000). 但是,John的C (2 ≤ C ≤ N)头牛们并不喜欢这种布局,而且几头牛放在一个隔间里,他们就要发生争斗。为了不让牛互相伤害。John决定自己给牛分配隔间,使任意两头牛之间的最小距离尽可能的大,那么,这个最大的最小距离是什么呢
【输入】
第一行:空格分隔的两个整数N和C;
第二行---第N+1行:i+1行指出了xi的位置。
【输出】
一个整数,最大的最小值。
【输入样例】
5 3 1 2 8 4 9
【输出样例】
3
【提示】
把牛放在1,4,8这样最小距离是3。
类似的最大值最小化或者最小化最大值的问题,通常用二分法就可以很好的解决。我们定义:
设C(d)表示可以安排牛的位置,并使得最近的两头牛的距离不小于d。
那么问题就转化为求满足C(d)的最大的d,另外,最近的间距不小于d也可以看成是所以牛的间距不小于d,因此就可以用C(d)表示可以安排牛的位置,并使得任意两头牛的距离不小于d。对于这个问题的判断,使用贪心法便可非常 容易地求解。
1.对牛舍的位置x进行排序;
2.把第一头牛放入x0的牛舍;
3.如果第i头牛放入了xj间牛舍,则第i+1头牛就要放入满足xj+d<=xk的最小的牛舍xk中。
对x的排序只需在最开始是进行一次就可以了,每一次判断对每头牛最多进行一次处理,因此算法的时间复杂度是O(nlogn)。
代码如下:
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; int read() { char ch=getchar(); int a=0,x=1; while(ch<'0'||ch>'9') { if(ch=='-') x=-x; ch=getchar(); } while(ch>='0'&&ch<='9') { a=(a<<3)+(a<<1)+(ch-'0'); ch=getchar(); } return a*x; } int n,m,x[100005]; bool check(int d) { int cow=1; //第一个牛舍放牛 int dis=x[1]+d; //后面的牛舍要满足大于等于dis才可以放牛 for(int i=2;i<=n;i++) { if(x[i]>=dis) { cow++; //放进一头牛 dis=x[i]+d; //找下一个牛舍应符合的条件 } } return cow>=m; //判断是否够m个 } int main() { n=read(); m=read(); for(int i=1;i<=n;i++) x[i]=read(); sort(x+1,x+1+n); //按照牛舍的位置从小到大排序 int l=0,r=x[n]-x[1]; while(l<=r) //二分找答案 { int mid=(l+r)/2; if(check(mid)) l=mid+1;//若check为true说明找的这个d偏小,我们要往右区间找 else r=mid-1; //否则则偏大,我们往左区间找 } cout<<r; //当前r就是最大的d return 0; }
简单说下二分思想:
1.二分的基本用途是在单调序列或单调函数中做查找操作,当问题的答案具有单调性时,就可以通过二分把求解转化成为判断(更具复杂度理论,可知判定的难度小于求解);
2.二分思想是不断将待求区间平分成两份,根据求解区间中点的情况来确定目标元素所在的区间,这样就把解的范围缩小了一半,时间复杂度O(二分次数*单次判定复杂度);
3.对于定义域在实数域为整数域的问题,可以用类似的方法,判断r-l的精度是否达到要求,即r-l>=eps,但由于实数运算带来的精度问题,若eps取得太小就会导致程序死循环,因此往往指定二分次数更好;
二分模板;
int erfen(int L,int R) { int L=1,R=n,ans; while(L<=R) { int mid=(R+L)/2; if(check(mid)) ans=mid,L=mid+1; else R=mid-1; } return ans; }