POJ2456 Aggressive cows 二分查找

Aggressive cows

Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 24079 Accepted: 11213
Description

Farmer John has built a new long barn, with N (2 <= N <= 100,000) stalls. The stalls are located along a straight line at positions x1,…,xN (0 <= xi <= 1,000,000,000).

His C (2 <= C <= N) cows don’t like this barn layout and become aggressive towards each other once put into a stall. To prevent the cows from hurting each other, FJ want to assign the cows to the stalls, such that the minimum distance between any two of them is as large as possible. What is the largest minimum distance?
Input

  • Line 1: Two space-separated integers: N and C

  • Lines 2…N+1: Line i+1 contains an integer stall location, xi

Output

  • Line 1: One integer: the largest minimum distance

Sample Input

5 3
1
2
8
4
9
Sample Output

3

问题连接

问题描述

给出牛棚和牛的数量,还有牛棚的位置,每两头牛之间有距离,使其中最小的距离尽可能的大,并输出最大的最小距离。

问题分析

解题思路

如果数据较少,可以一个个枚举出不同的放牛的方式,找到每种方式中最小的距离,再从所有方式中找出最大的距离,但显然这么做会超时。
有个好办法,二分查找。因为二分查找最大的特点就是可以通过条件判断很快地在一个范围内找出所要的解。
记要找的那个值为D,最左边的牛棚到最右边的牛棚的距离记为dm。因为给出牛棚的位置,就可以知道任意两个牛棚的间距,显然要找的值D是在距离范围[1,dm]的。实际上,根据牛的数量c,还可以缩小为[1,dm/(c-1)]。那么我们要找的D可以通过一定的“条件”,用二分查找从这个范围中找到。

条件:
给出一个距离d,现有范围[L,R],我们要判断用这个d能不能实现将c头牛全都放到牛棚里,具体判断方式是:先取最左边的牛棚,再找与它相邻的牛棚,如果两牛棚的距离小于d,说明这个牛棚不符合我们的要求,就需要继续找下一个相邻的牛棚;否则这个牛棚符合我们的要求,将牛放进去,计数,并找下一个相邻的牛棚。如果在遍历完所有牛棚之前能将牛全部放进去,则这个d是可以实现的。
如果可以做到,那么说明这个d可能是我们要找的D,范围缩小到[d,R],这样可以保证d还在求解的范围内,又缩小了寻找的范围。否则,缩小到[L,d-1]。将得到的新范围看成是原来的范围[L,R],新的d取L和R的中值,按上面的方法处理,那么最后会出现[L,R],L=R的情况,这时说明要找的D就是这个L或R。

解题思路如上。

mid取值的讨论

下面关于mid,也就是上面的d,是取(l+r)/2,还是取(l+r+1)/2的讨论。(因为没搞懂这个时二分查找是容易出现死循环的,也让我苦恼了许久)
下不讨论循环体内l=mid+1且r=mid-1的情况和l=mid,r=mid的情况,只讨论l,r不同时用mid赋值的情况。

mid=(l+r)/2的情况:

注意mid=(l+r)/2,是整数的除法,如果得到是小数,最后会向下取整,下用“[]”表示向下取整(取整函数[x]).
若l+r为偶数,则mid=[(l+r)/2],有2×mid=l+r。
令mid=l,则有l=r,这时是二分寻找的循环的跳出条件,所以不会产生死循环。
令mid=r,则也有l=r,同上,不会出现死循环。
若l+r为奇数,则mid=[(l+r)/2]=[(l+r-1)/2],有2×mid=l+r-1。
令mid=l,则有l=r-1,也就是说明当l等于r-1时,若循环体内对l赋值mid,则mid会与l相互赋值,所以出现死循环,一直跳不出来。
令mid=r,则有r=l-1,则在l==r的时候就跳出循环了,不会出现死循环。

//可能会出现死循环(当l==r-1时,mid=l,l=mid,mid=l,l=mid......)
while (l < r)
{
	mid = (l + r) / 2;
	if (满足条件) l = mid;
	else r = mid - 1;
}
//不会出现死循环
while (l < r)
{
	mid = (l + r) / 2;
	if (满足条件) l = mid+1;
	else r = mid;
}
mid=(l+r+1)/2的情况:

若l+r+1为偶数,则mid=[(l+r+1)/2],有2×mid=l+r+1。
令mid=l,则有l=r+1,而在l == r的时候就跳出循环了,所以不会出现死循环。
令mid=r,则有r=l+1,说明当r等于l+1时,且循环体内对r赋值mid,出现mid与r互相赋值的死循环
若l+r为奇数,则mid=[(l+r+1)/2]=[(l+r)/2],有2×mid=l+r。
令mid=l,则有l=r,不会出现死循环。
令mid=r,则有r=l,不会出现死循环。

//不会出现死循环
while (l < r)
{
	mid = (l + r + 1) / 2;
	if (满足条件) l = mid;
	else r = mid - 1;
}
//可能出现死循环(当r==l+1时,mid=r,r=mid,mid=r,r=mid......)
while (l < r)
{
	mid = (l + r + 1) / 2;
	if (满足条件) l = mid + 1;
	else r = mid;
}
结论

从上述分析,可以知道出现死循环的情况有两种,一种是mid=(l+r)/2且l=mid的情形;另一种是mid=(l+r+1)/2且r=mid的情形。所以在实际使用的时候,就应该避免遇到死循环。
如果要用mid对l赋值的时候,选用mid=(l+r+1)/2;
如果要用mid对r赋值的时候,选用mid=(l+r)/2.

程序如下

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N = 100005;
int n, c; 
int x[N];
int bfind();
bool judge(int d);
int main()
{
	scanf_s("%d %d", &n, &c);
	for (int i = 1; i <= n; i++)
		scanf_s("%d", x + i);
	sort(x + 1, x + n + 1);
	printf("%d\n", bfind());
	return 0;
}
int bfind()//二分查找
{
	int l, r, mid;
	l = 1;
	r = (x[n] - x[1]) / (c - 1);//起始搜索范围,c头牛需要(c-1)个间隔,得到可能的最大间隔
	while (l < r)
	{
		mid = (l + r + 1) / 2;
		if (judge(mid)) l = mid;//表示mid可以实现,取左边区域,因为要找到最大的最小距离
		else r = mid - 1;//表示mid不可以实现,取左区域
	}
	return l;
}
bool judge(int d)//判断
{
	int i, a, count;
	a = x[1];//第一头牛放到x[1]里
	count = 1;//放了一头牛
	for (i = 2; i <= n; i++)
	{
		if (x[i] - a >= d)
		{
			a = x[i];
			count++;
		}
		if (count == c) return true;
	}
	return 0;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值