目录
二分法
用于有单调性,数据范围大
用到二分的四种情况
1:找大于等于数的第一个位置 (满足某个条件的第一个数)
2:找小于等于数的最后一个数 (满足某个条件的最后一个数)
3.查找最大值 (满足该边界的右边界)、
4.查找最小值 (满足该边界的左边界)
思想
while让每次区间缩小一半直到等于所求的点,难点在于分为两个区间,与题目有关
先取一个区间的中点,与要找的值比较,如果值比较小,说明在中点的左边,更新右端点
r=mid ,在找到起点的条件下继续更新左端点l=mid+1,直到第一个点
如果第一次二分在中点的右边,更新左端点l=mid,那么继续二分更新右端点r=mid-1
选择二分模板
可以实现找到符合条件的最大值和最小值,题目要求最小值用第一个模板,即(l+l)/2,
题目 要求符合条件的最大值,用第二个模板,即(l+l+1)/2,第二个模板就是求满足条件的最后一个数(一般是小于等于)
1.第一种写法(口诀:有减必有加)
mid=(l+r)/2
while(l<r){
int mid=(l+r)/2;
if(a[mid]>=x) r=mid; //注意if语句里面是等于
else l=mid+1;
}
cout<<l;
mid =(l+r+1)/2
while(l<r){
int mid=(l+r+1)/2;
if(a[mid]<=x) l=mid; //注意if语句里面是等于
else r=mid-1;
}
cout<<l;
2.第二种写法
while(l<=r){ //区别
int mid = (l+r) / 2;
if (a[mid] <= x) l = mid + 1;
else if (a[mid] >= x) r = mid - 1;
}
cout<<l;
二分函数
查找首个不小于给定值的元素的函数lower_bound 和查找首个大于给定值的元素的函数 upper_bound,二者均定义于头文件 <algorithm> 中
定义:在从小到大的排序数组中,lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于cnt的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
注1:为什么要 - 数组起始点,因为二分函数返回的是一个迭代器,要减去数组起始点才是这个点的距离。
注2:写法不同
前提,元素从小到大排序
1.vector,数组
int pos=lower_bound(Vector.begin(),Vector.end(),val)-Vector.begin();
int pos=lower_bound(Arr,Arr+数组的大小,val)-Arr;
2.set,map(本身是按照key从小到大排序)
set<int>::iterator it;
it=Set.lower_bound(val);
map<int,int>::iterator it;
it=Map.lower_bound(val);
注3:用法
int num[6]={1,2,4,7,15,34};
sort(num,num+6); //按从小到大排序
int pos1=lower_bound(num,num+6,cnt)-num; //返回数组中第一个大于或等于被查数的值
int pos2=upper_bound(num,num+6,cnt)-num; //返回数组中第一个大于被查数的值
sort(num,num+6,cmd); //按从大到小排序
int pos3=lower_bound(num,num+6,cnt,greater<int>())-num; //返回数组中第一个小于或等于被查数的值
int pos4=upper_bound(num,num+6,cnt,greater<int>())-num; //返回数组中第一个小于被查数的值
vector<int> v;
sort(v,begin(),v.end());
int t;
int pos=lower_bound(v.begin(),v.end(),t)-v.begin(); //查找第一个大于等于给定值 t 的元素
int pos=upper_bound(v.begin(),v.end(),t)-v.begin(); //查找第一个大于给定值 t 的元素
int pos=lower_bound(v.begin(),v.end(),t)-v.begin()-1; //查找最后一个小于给定值 t 的元素
int pos=upper_bound(v.begin(),v.end(),t)-v.begin()-1; //查找最后一个小于等于给定值 t 的元素
小数二分
double l=-100,r=100;
while(r-l>1e-8){
double mid=(l+r)/2;
if(check(mid)){
l=mid;
}
else r=mid;
}
printf("%.6lf\n",l);
例题
1. 整数二分 acwing 789 数的范围
题目 : https://www.acwing.com/problem/content/791/
代码:
#include<iostream>
using namespace std;
const int N=1e5+5;
int n,q;
int a[N];
int main(){
cin>>n>>q;
for(int i=0;i<n;i++){
cin>>a[i];
}
while(q--){
int x;
cin>>x;
int l=0,r=n-1;
while(l<r){
int mid=(l+r)/2;
if(a[mid]>=x) r=mid; //查找不小于x的第一个位置
else l=mid+1;
}
if(a[l]!=x) cout<<"-1 -1"<<"\n";
else{
cout<<l<<' ';
int l=0,r=n-1;
while(l<r){
int mid=(l+r+1)/2;
if(a[mid]<=x) l=mid;
else r=mid-1;
}
cout<<l<<"\n";
}
}
return 0;
}
2. 浮点数二分 acwing 790 数的三次方根
题目 : https://www.acwing.com/problem/content/792/
代码:
#include<bits/stdc++.h>
#include<cstdio>
using namespace std;
int main(){
double x;
cin>>x;
double l=-10000,r=10000;
while(r-l>1e-8){
double mid=(l+r)/2;
if(mid*mid*mid>=x) r=mid;
else l=mid;
}
printf("%lf\n",l); //double默认保留小数点后6位
return 0;
}
练习: 洛谷 P8647 分巧克力 https://www.luogu.com.cn/problem/P8647
洛谷 P1923 求第k小的数 https://www.luogu.com.cn/problem/P1923
三分法
题目 : https://www.luogu.com.cn/problem/P3382
代码
// Problem: P3382 【模板】三分法
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3382
// Memory Limit: 128 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N = 200;
int n;
double l,r;
double a[15];
//三分法模板
double check(double x){ //秦九韶算法,形似an*(x^n)+an-1*(x^n-1)+an-2*(x^n-2)...
double sum=0;
for(int i=n;i>=0;i--){
sum=sum*x+a[i];
}
return sum;
}
void s(){
cin>>n>>l>>r;
for(int i=n;i>=0;i--){
cin>>a[i];
}
while(r-l>=1e-7){ //三分法
double mid=(l+r)/2;
if(check(mid+1e-7)>check(mid-1e-7)) l=mid;
else r=mid;
}
printf("%.5lf",l);
}
int main(){
int T=1;
//cin>>T;
while(T--){
s();
}
return 0;
}
三分函数
题目: https://www.luogu.com.cn/problem/P1883
代码
// Problem: P1883 函数
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1883
// Memory Limit: 125 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+5;
int n;
int a[N],b[N],c[N];
double check(double x){
double ans=0;
for(int i=1;i<=n;i++){
ans=max(ans,a[i]*x*x+b[i]*x+c[i]);
}
return ans;
}
int main(){
int T;
cin>>T;
while(T--){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i]>>b[i]>>c[i];
}
double l=0,r=1000;
while(r-l>=1e-9){
double m1=l+(r-l)/3,m2=r-(r-l)/3;
if(check(m1)>check(m2)){
l=m1;
}
else r=m2;
}
printf("%.4lf\n",check(l));
}
return 0;
}