二分专题【二分查找】

二分专题

今天训练的是二分查找专题,刷完专题后对二分法的应用有了新的理解。

在平时写题中,有些时候会遇到题目要求给出诸如给定划分方式范围内的最大值或是最短距离的最大值等,此类问题枚举虽然可以理论上解决,但是由于题目一般数据量较大,暴力枚举没有什么AC机会。在这里便用到了时间复杂度为logN的更高效率的方法:二分查找。

在有序表中查找元素时常常使用二分查找法,因此在遇到此类题目时若题目条件允许变动给出数组的元素位置,对元素进行排序会有益于解题。二分法基于逐步缩小范围的思维方法,每次二分都会使搜索范围缩小一半,在有序的元素中快速接近要查找的元素。 


目录

二分专题

A - Aggresive Cows

           二分查找的方法

B - Drying

C - Count on Canton

D - River Hopscotch

E - Monthly Expence

F - Pie

G - Expanding Rods


 

A - Aggresive Cows

Time limit

1000 ms

Memory limit

65536 kB

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

Hint

OUTPUT DETAILS: 

FJ can put his 3 cows in the stalls at positions 1, 4 and 8, resulting in a minimum distance of 3. 

Huge input data,scanf is recommended.

题意

农夫约翰有一群C只不安分的牛,他们对约翰的牛栏有很大意见,导致他们被放入牛栏后就会开始斗牛,为了不出牛命,约翰想要求出给定的N个直线排列的牛栏中,每只牛被放入后两牛之间最小距离的最大值是多少。

思路

最小距离的最大值,emmmmmm,你说的很好,那就二分查找吧。首先将所有牛栏的位置读入后按位置排序,得到适用于二分查找的升序表。

二分查找的方法

那么现在问题来了눈_눈,说是二分法,对啥玩意儿进行二分啊?此处我们要查找的量是距离,那么应该用可以取到的距离的最大值和最小值进行二分。最大距离就是整个牛栏的长度,也就是第一个牛栏到最后一个牛栏的距离,最小距离,可求出每个牛栏之间能取到的最小距离,但是敲代码好麻烦.........就取0吧。于是我们得到了二分的两个端点,small和large(其实更形象的应该用left和right,左右端点嘛),接下来我们可以得出两端点的中点mid=(small+large)/2,这个mid便是我们本次要尝试将牛放进去的最小间隔距离。怎么放呢?第一个牛栏不管怎么说肯定是得放的对吧,第二个牛栏要不要放呢?看看第二个牛栏的位置和第一个牛栏差值是不是大于等于mid,发现是小于的,放不了。既然第二个牛栏放不了,我们就要比较第一个和第三个牛栏间的距离够不够放,这次够了,我们往第三个牛栏里塞一头吧。接下来比较的就不是第一个牛栏和第四个牛栏间的距离了,而是刚刚放入牛的第三个和第四个间的距离,原因你懂的。因此我们需要一个变量来存储最近一次放入牛的牛栏位置。

这样我们每碰到一个符合要求的牛栏就塞一头牛,一轮遍历完所有牛栏后,得到了在最小距离为mid时能放进牛栏的牛总数。但是我们可能发现能放的数目大于我们拥有的牛数目,说明其实我们可以让间隔更大点的,这个最小距离mid开小了。于是让small=mid+1,将下一轮搜索的区间定在刚刚搜索完区间的右半段,也就是数值较大的半段。亦或是我们得到了能放的数目小于我们拥有的牛数,说明范围开太大了,于是large=mid-1,搜索刚刚的左半段。还有一张情况就是我们得到了刚好等于目前拥有牛数的可放入数,但是我们不能返回,我们要贪心一点,得试试要是距离再开大一点点,是不是还是得到这个数,于是依然small=mid+1,往大的半段搜索。当我们一路搜索到large<mid时,搜索就要结束了,返回一个值。这个值是large还是mid还是small呢?就要看最后我们得到符合要求的距离后的“再开大一点点”是往哪边开了,如果我们往右开,也就是small=mid+1,那么small就变了,我们得返回large,如果我们往左开,large=mid+1,我们改动了large,说明得返回small。

上面的两段恰好可以切分为两个函数,上面一段的主要操作是在每个给定最小距离mid中判断放下了多少牛,下面一段的主要操作是判断上面一段给出的可放牛数是否符合要求并依此求出新的mid继续让上一段尝试放牛。于是函数功能,传递的参数和返回值我们都清楚了,是时候写函数了。

//这里是第一段,判断在给定最小距离的情况下能塞下多少头牛
int putcows(int dis)
{
    //p储存最近一次放入牛的牛栏位置,我们在初始化时就在第一个牛栏中放牛了,因此计数变量=1
	int counts = 1, p = stall[1];
	for(int i=2;i<=n;i++)
	{
        //如果当前牛栏和上次放牛的牛栏间距大于最小距离,放牛计数器+1,最近放牛的牛栏为当前牛栏
		if (stall[i] - p >= dis)
		{
			counts++;
			p = stall[i];
		}
	}
    //把统计好的可放牛数目传回去
	return counts;
}

//这里是第二段,负责最初给出mid值,并通过传回的可放牛数调整接下来给出的mid值
int find()
{
    //初始化最初的端点
	int small = 0, large = stall[n] - stall[1], mid;
	while(small<=large)
	{
		//最小值取为中间
		mid = (small + large) / 2;
		//通过函数返回在当前最小值情况下能够放进的牛数目
		int puts = putcows(mid);
		//此处不能多写(puts==c)return mid;遇到数目等于应当贪心一点把最小值扩大试试看
		if (puts >= c)small = mid + 1;
		else if (puts < c)large = mid - 1;
	}
    //把未被贪心步骤改变的那个值传回主函数
	return large;
}

至此二分查找的写法就出来了,我们再写个主函数,添加点全局变量啥的就可以愉快的去 WA AC了。

AC代码

#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <cmath>
#include <cstring>
#include <string>
#include <list>
#include <algorithm>
#include <set>
#include <list>
#define N 100005

using namespace std;

int stall[N];
int n, c;

int putcows(int dis)
{
	int counts = 1, p = stall[1];
	for(int i=2;i<=n;i++)
	{
		if (stall[i] - p >= dis)
		{
			counts++;
			p = stall[i];
		}
	}
	return counts;
}

int find()
{
	int small = 0, large = stall[n] - stall[1], mid;
	while(small<=large)
	{
		//最小值取为中间
		mid = (small + large) / 2;
		//通过函数返回在改最小值情况下能够放进的牛数目
		int puts = putcows(mid);
		//此处不能多写(puts==c)return mid;遇到数目等于应当继续往下判
		if (puts >= c)small = mid + 1;
		else if (puts < c)large = mid - 1;
	}
	return large;//return small-1;
}

int main()
{
	while(scanf("%d%d",&n,&c)!=EOF)
	{
		int i;
		for(i=1;i<=n;i++)
		{
			scanf("%d", &stall[i]);
		}
        //对胡乱输入的牛栏位置排序
		sort(stall + 1, stall + n + 1);
        //find()函数为查找函数
		printf("%d\n", find());
	}
}

 


B - Drying

Time limit

2000 ms

Memory limit

6

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值