浮点数、整数二分法
一、概念
1.二分适用范围:
一个整体可以被分成两部分,此时所有事物必须属于这两部分中的一方,且没有事物可以同时属于双方,即满足【互补】【互斥】的特点。
2.二分关键:
找到中间值(分割这个整体的判断条件)。
二、实例
下面来看看这两道算法题:
题目1——剪绳子【浮点数二分】
来源:AcWing 680. 剪绳子
思路:
分析题目,已知的是绳子的最终数量,求最大长度,这种情况很难求出来,但若是已知绳子长度反推绳子数量就很好求了,通过求出来的绳子数量与题目要求的进行对比,就可以推断出我们假设的长度是否符合题意了。
中间值:长度mid,如果通过mid求出来的绳子数量小于题目要求,则最终答案小于mid,否则将大于等于mid。
其余问题:
1.缩短到什么范围可以得到答案?
一般求到题目要求的精度再加两个精度即可,或通过时间复杂度来计算,竞赛时c++最大时间复杂度在1e7~1e9范围内,以1e7为最佳求得最多可以循环多少次不会超时。
AC的代码:
#include<iostream>
using namespace std;
const int N=100010;
int g[N];
int n,m;//绳子个数,需求绳子个数
bool check(double d){
int res=0;
for(int i=0;i<n;++i){
int temp=g[i]/d;
res+=temp;
}
return res>=m;
}
int main(){
cin>>n>>m;
for(int i=0;i<n;++i)
cin>>g[i];
double min1=0,max1=1e9;
while(max1-min1>1e-4)//因为要求精度是小数点后k位,按照经验来说求到1e-(k+2)即可 用for循环也行,循环100次
{
double mid=(min1+max1)/2;
if(check(mid)) min1=mid;
else max1=mid;
}
printf("%.2lf\n",max1);
return 0;
}
题目2——分巧克力【整数二分】
来源:AcWing 1227. 分巧克力
思路:
大致思路和上一题一样,唯一需要注意的是当中间值不为整数时,应该将mid归为哪个区间。
这里有两个模板建议背熟:
模板1:我们将区间 [ l, r ] 分为 [ l, mid] 和 [mid + 1 , r], 此时求 mid 时不用 +1
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;//等价于mid = ( l + r ) / 2;
if (check(mid)) r = mid;
else l = mid + 1;
}
return l;
}
模板2:我们将区间 [l, r] 分为 [l, mid-1] 和 [mid, r], 此时为了防止死循环,所以求 mid 时需要 + 1
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
什么时候用模板1,什么时候用模板2?
这个是不固定的,需要具体题目具体分析。但有个大大说当我们在找最小值边界的时候一定得用模板1,在找最大值边界时一定得用模板2,,这个先记住吧。
AC的代码:
#include<iostream>
using namespace std;
typedef long long LL;
const int N=100010;
int n,m;
int h[N],w[N];
bool check(int mid){
LL res=0;
for(int i=0;i<n;++i){
res+=(LL)h[i]/mid*(w[i]/mid);
if(res>=m) return true;
}
return false;
}
int main(){
cin>>n>>m;
for(int i=0;i<n;++i) scanf("%d%d",&h[i],&w[i]);
int l=1,r=1e5;//保证了每个小朋友至少有一块蛋糕,所以最小值为1
while(l<r){
int mid=l + r + 1 >> 1;//上取整
if(check(mid)) l=mid;
else r=mid-1;
}
printf("%d\n",r);
return 0;
}
好啦,感觉好绕~多刷点这种题大概就会了叭。