【算法】二分和三分

目录

二分法

用到二分的四种情况

思想

选择二分模板

二分函数

小数二分 

 例题

  三分法 

 三分函数


二分法

        用于有单调性,数据范围大

用到二分的四种情况


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;	

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值