• 一本通网站 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;
    } 
  • 相关阅读:
    课后作业
    使用类的静态字段和构造函数,我们可以跟踪某个类所创建对象的个数。请写一个类,在任何时候都可以向它查询“你已经创建了多少个对象?”。
    课程作业·02
    课程作业01
    课程作业02 将课程中的所有动手动脑的问题以及课后实验性的问题,整理成一篇文档。
    课程作业01 模仿JavaAppArguments.java示例,编写一个程序,此程序从命令行接收多个数字,求和之后输出结果。
    《大道至简》第一章伪代码
    Vue2.0版英雄联盟助手,我的第一个小开源项目
    二级下拉菜单的三种实现方法——CSS 、JS、 jQuery
    关于清除浮动 and position的一些注意点
  • 原文地址:https://www.cnblogs.com/xcg123/p/10990458.html
Copyright © 2020-2023  润新知