二分的简单理解和使用方法

本文介绍了二分法的基本原理,并通过实际案例演示如何在查找满足特定条件的数组元素(如大于某个值的最小值或小于某个值的最大值)时快速定位。涉及到了切分巧克力问题和实数次根号计算的二分查找技巧,展示了二分法在算法中的实用性。
摘要由CSDN通过智能技术生成

1.首先,二分法的原理:举个简单的例子就是,我们猜价格游戏,有一件商品,让你去猜它的价格,会给出价格的区间,比如说100到900之间,那么我们如何以最快的速度准确的猜出商品的价格呢。我们首先先猜100和900中间的那个数500,然后看是比500比商品的价格高了还是低了,如果高了,我们接下来就在100和500之间猜,重复上述步骤,一步一步地缩小范围,直到找到商品的价格。

补充:利用这个原理,我们在寻找数据时可以大大减少计算量,这也是我们使用二分算法的另一个重要原因。

2.接下来再看一下如何应用:需要使用二分法的题目,有两类。

第一类:求在一个数组中,a[i]>某个常数的最小值

第二类:求在一个数组中,a[i]<某个常数的最大值

需要满足的条件:

(1)有序(要查找的数列或一堆数需要有序)

(2)求在一个数组中,a[i]>某个常数的最小值或者求在一个数组中,a[i]<某个常数的最大值

如果题目满足上述的要求的一般都可以使用二分法来做。

3.下面举个例子

(1)分巧克力(蓝桥杯真题)(数据都是整数)

儿童节那天有K位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。
    小明一共有N块巧克力,其中第i块是Hi x Wi的方格组成的长方形。

    为了公平起见,小明需要从这 N 块巧克力中切出K块巧克力分给小朋友们。切出的巧克力需要满足:

    1. 形状是正方形,边长是整数  
    2. 大小相同  

例如一块6x5的巧克力可以切出6块2x2的巧克力或者2块3x3的巧克力。

当然小朋友们都希望得到的巧克力尽可能大,你能帮小Hi计算出最大的边长是多少么?

输入
第一行包含两个整数N和K。(1 <= N, K <= 100000)  
以下N行每行包含两个整数Hi和Wi。(1 <= Hi, Wi <= 100000) 
输入保证每位小朋友至少能获得一块1x1的巧克力。   

输出
输出切出的正方形巧克力最大可能的边长。

样例输入:
2 10  
6 5  
5 6  

样例输出:
2
 

代码如下:

#include<iostream>
using namespace std;
const int MAXN=100010;
int n,k;
int h[MAXN],w[MAXN];//high和low是下标,还是元素 
bool pd(int l)
{
	int sum=0;
	for(int i=0;i<n;i++)
	{
		sum+=(h[i]/l)*(w[i]/l);//求一共可以找到多少这样的正方体 
		if(sum>=k)//提前出来 
		{//如果已经分够了每个小朋友一块 
			return true; 
		}
	}
	return false; 
}
int main()
{
	cin>>n>>k;
	int mid; 
	for(int i=0;i<n;i++)//输入每一块巧克力的长宽 
	{
		cin>>h[i]>>w[i];
	}
	int high=0;//二分查找的上限
	for(int i=0;i<n;i++)
	{
		high=max(high,h[i]);//high被赋值之后,得到的是最大的,所以它求的是两个数组中最大的那一个 
		high=max(high,w[i]);
	 } 
	 int low=1;//由题意得下限至少为一
	  while(low<high)
	  {
		mid=(low+high+1)/2;
		if(pd(mid))
		{
			low=mid;
		}
		else
			high=mid-1;//mid都不行,那直接让high减一,可以减小计算量
	  }
	  cout<<low;//low和high输出哪个都一样 
	  return 0;
}

补充一下:mid那里加一是因为当为3和4时,由于数据是int型的,会卡在循环里出不来(还体现了一点贪心的思想)
 另外,如果是求大于等于某个数的最小数时不用加一。 

(2)实数的二分

求解m的n次根号下的值,例如27开三次根号,保留7位小数

代码如下:

#include <cstdio>
#include <iostream>
using namespace std;
double n,l,r,mid;
double eps=1e-8;//10的负八次方,用于精度控制 
bool pd(double a,int m)
{
	double c=1;
	while(m>0)
	{
		c=c*a;
		m--;
	}
	if(c>=n)
		return true;
	else
		return false;
}
int main()
{
	int m;
	cin>>n>>m;
//设置二分边界
	l=0,r=n;
//实数二分
	while (l + eps < r)
	{
		double mid = (l + r) / 2;//实数不需要加一 
		if (pd(mid,m))
			r = mid;
		else
			l = mid;
	}
	printf("%.7f",l);
	//printf("%x.yf",n),其中X是固定整数长度,小数点前的整数位数不够,会在前面补0 
	//y是保留小数位数,不够补零
	return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值