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;
}