一本通网站 1433:【例题1】愤怒的牛 及 二分小结

原题 传送门

【题目描述】

农夫 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;
} 
原文地址:https://www.cnblogs.com/xcg123/p/10990458.html