7-5 愤怒的牛

该文章介绍了一个关于优化牛舍布局的问题,农夫约翰需要将牛放在牛舍中以最大化牛之间的最小距离。通过排序牛舍的位置,利用二分查找在给定区间内找到能容纳所有牛的最大最小距离。文章详细解释了二分查找的实现过程和判断条件,以及最终的AC代码实现。
摘要由CSDN通过智能技术生成

7-5 愤怒的牛

农夫约翰建造了一座有n间牛舍的小屋,牛舍排在一条直线上,第i间牛舍在x i​的位置,但是约翰的m头牛对小屋很不满意,因此经常互相攻击。约翰为了防止牛之间互相伤害,因此决定把每头牛都放在离其它牛尽可能远的牛舍。也就是要最大化最近的两头牛之间的距离。

牛们并不喜欢这种布局,而且几头牛放在一个隔间里,它们就要发生争斗。为了不让牛互相伤害。John 决定自己给牛分配隔间,使任意两头牛之间的最小距离尽可能的大,那么,这个最大的最小距离是多少呢?

输入格式:
第一行用空格分隔的两个整数n和m,n,m<=10^5 ;

第二行为n个用空格隔开的整数,表示位置x i。x i <MAXINT

输出格式:
输出仅一个整数,表示最大的最小距离值。

输入样例:
5 3
1 2 8 4 9
输出样例:
3

思路:排序加二分查找

首先对小屋的位置进行排序,然后计算出第一个小屋和最后一个小屋之间的距离(house[n-1]-house[0] )
而题目要求的“最大的最小距离值”答案一定在 [ 1 , house[n-1]-house[0] ] 这个闭区间内, 我们对此区间进行枚举寻找符合条件的最大值(条件在下文“确定判断条件”中说明),但是一般的一个个的枚举一定会超时,而二分查找用于在一个单调或者局部单调有序数组中查找一个符合某些条件的最优值,时间复杂度为O(logN),正是解决这类问题的算法。

二分法最重要的两个点:

  • while循环中 left 和 right 的关系,到底是 left <= right 还是 left < right 迭代过程中

  • middle 和 right 的关系,到底是 right = middle - 1 还是 right = middle

本题中我设置的 left 和 right 的关系为 left <= right,符合条件时left=middle+1 , 否则 right=middle-1,这样设置跳出循环时left = right+1,导致这种情况只有俩个可能:

  • 条件判断成功,此时left=right,left=middle+1
  • 条件判断失败,此时left=right,right=middle-1

此时的right一定是最后一次判断条件成功的值:

  • judge(middle):条件判断函数
while(left<=right){
        int middle = (left+right)/2;
        if(judge(middle)){
            left=middle+1;
        }
        else{
            right=middle-1;
        }
}
  

确定判断条件

在一般的经典二分题目,一个有序区间寻找是否存在一个数时,条件只需要判断middle和寻找数的大小关系再进行迭代,如此题:704. 二分查找,本题中我们判断的是当前二分枚举的值(x)是否符合条件,即以x为最小的间隔来安排牛进牛舍,统计最后一共能有多少头牛能进入宿舍,结果大于等于牛的总数即符合条件:

  • house:房子的位置数组
  • x:二分枚举的距离的“最小的最大值”
  • last:上一个牛入住的牛舍位置下标,基于贪心,下标0的第一个牛舍必然安排牛入住,last=0
  • sum:当前入住牛数目,sum初始为1
bool judge(int x){
	
    int last=0,sum=1;  
    for(int i=1;i<n;i++){
        if(house[i]-house[last]>=x){
            last=i;
            sum++;
        }
    }
    return sum>=m? true:false;    
}

AC代码:

#include<bits/stdc++.h>
using namespace std;
vector<int> house;
int n,m;
bool judge(int x){
	
    int last=0,sum=1;  
    for(int i=1;i<n;i++){
        if(house[i]-house[last]>=x){
            last=i;
            sum++;
        }
    }
    return sum>=m? true:false;  
}
int main(){
    
    cin>>n>>m;
    house = vector<int>(n);
    for(int i=0;i<n;i++){
        cin>>house[i];   
    }
    sort(house.begin(),house.end());
    
    int left = 1 , right = house[n-1]-house[0];
    while(left<=right){
        int middle = (left+right)/2;
        if(judge(middle)){
            left=middle+1;
        }
        else{
            right=middle-1;
        }
	}
    cout<<right;
    return 0;
    
    
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值