二分和三分

二分和三分

标签(空格分隔): @zhshh cpp OI

回到顶部

二分问题

模板

离散二分答案

int check (int x){
    //检查x是否符合,符合 真,不符合 假
}
int search (int l,int r){
    int mid;
    while(l<=r){
        mid=(l+r)/2;
        if(check(mid)){
            l=mid+1;
            ans=mid;
        }else{
        r=mid-1;
        }
    }
    return ans;
}

可以看到,假定一个\(x\)充分小,可以使得\(check(x)\)成立,那么明显,在\(search(l,r)\)中,存在\(x_0\)使得\(x \in [l,x_0],check(x)=true\),\(x \in (x_0,r],check(x)=false\),注意\(x_0\)是从\(l\)开始最大的满足的值。
也就是如果改成\(!check()\),那么就是\(check(x)=true\)是从\(x_{true}\)连续到r的,即\(x \in [l,x_0],!check(x)=true,x\in[x_{true},r],!check(x)=false\),因此二分求解的\(answer\)\(x_0\),而最小成立值\(x_{true}=x_0+1\),因此返回时要return ans+1下面代码。

int check (int x){
    //检查x是否符合,符合 真,不符合 假
}
int search (int l,int r){
    int mid;
    while(l<=r){
        mid=(l+r)/2;
        if(!check(mid)){
            l=mid+1;
            ans=mid;
        }else{
        r=mid-1;
        }
    }
    return ans+1;
}

连续二分答案

很简单的二分,边界条件也不需要十分注意。最后return时,无论是left,right还是mid都可以,因为\(mid=(left+right)/2\)而此时mid,left,right,都是比较接近(最大差值eps)的。

#define eps 1e-6
int check (int x){
    //检查x是否符合,符合:真,不符合:假。
}
double search(){
    double mid;
    while(fabs(left-right)>eps){
        mid=(left+right)/2;
        if(check(mid)){
            right=mid;
        }else{
            left=mid;
        }
    }
    return left;
}

离散二分查找

转载自你真的会写二分查找吗 By luoxn28
下面的都是针对直接sort后的不严格单调递增的序列
下面六种本质区别就是一个恰当的check函数

1 查找第一个与key相等的元素

  查找第一个相等的元素,也就是说等于查找key值的元素有好多个,返回这些元素最左边的元素下标。

// 查找第一个相等的元素
static int findFirstEqual(int[] array, int key) {
    int left = 0;
    int right = array.length - 1;

    // 这里必须是 <=
    while (left <= right) {
        int mid = (left + right) / 2;
        if (array[mid] >= key) {
            right = mid - 1;
        }
        else {
            left = mid + 1;
        }
    }
    if (left < array.length && array[left] == key) {
        return left;
    }
    return -1;
}

2 查找最后一个与key相等的元素

  查找最后一个相等的元素,也就是说等于查找key值的元素有好多个,返回这些元素最右边的元素下标。

// 查找最后一个相等的元素
static int findLastEqual(int[] array, int key) {
    int left = 0;
    int right = array.length - 1;

    // 这里必须是 <=
    while (left <= right) {
        int mid = (left + right) / 2;
        if (array[mid] <= key) {
            left = mid + 1;
        }
        else {
            right = mid - 1;
        }
    }
    if (right >= 0 && array[right] == key) {
        return right;
    }
    return -1;
}

3 查找最后一个等于或者小于key的元素

  查找最后一个等于或者小于key的元素,也就是说等于查找key值的元素有好多个,返回这些元素最右边的元素下标;如果没有等于key值的元素,则返回小于key的最右边元素下标。

// 查找最后一个等于或者小于key的元素
static int findLastEqualSmaller(int[] array, int key) {
    int left = 0;
    int right = array.length - 1;

    // 这里必须是 <=
    while (left <= right) {
        int mid = (left + right) / 2;
        if (array[mid] > key) {
            right = mid - 1;
        }
        else {
            left = mid + 1;
        }
    }
    return right;
}

4 查找最后一个小于key的元素

  查找最后一个小于key的元素,也就是说返回小于key的最右边元素下标。

// 查找最后一个小于key的元素
static int findLastSmaller(int[] array, int key) {
    int left = 0;
    int right = array.length - 1;

    // 这里必须是 <=
    while (left <= right) {
        int mid = (left + right) / 2;
        if (array[mid] >= key) {
            right = mid - 1;
        }
        else {
            left = mid + 1;
        }
    }
    return right;
}

5 查找第一个等于或者大于key的元素

  查找第一个等于或者大于key的元素,也就是说等于查找key值的元素有好多个,返回这些元素最左边的元素下标;如果没有等于key值的元素,则返回大于key的最左边元素下标。

// 查找第一个等于或者大于key的元素
static int findFirstEqualLarger(int[] array, int key) {
    int left = 0;
    int right = array.length - 1;

    // 这里必须是 <=
    while (left <= right) {
        int mid = (left + right) / 2;
        if (array[mid] >= key) {
            right = mid - 1;
        }
        else {
            left = mid + 1;
        }
    }
    return left;
}

6 查找第一个大于key的元素

  查找第一个等于key的元素,也就是说返回大于key的最左边元素下标。

// 查找第一个大于key的元素
static int findFirstLarger(int[] array, int key) {
    int left = 0;
    int right = array.length - 1;

    // 这里必须是 <=
    while (left <= right) {
        int mid = (left + right) / 2;
        if (array[mid] > key) {
            right = mid - 1;
        }
        else {
            left = mid + 1;
        }
    }
    return left;    
}

7 二分查找变种总结

#include <iostream>
#include <cstdio>
#define MX 10010
using namespace std;
int a[MX];
int n;
int FindFirstEqual(int key){
	int left=1;
	int right=n;
	while(left<=right){
		int mid=(left+right)/2;
		if(a[mid]>=key){
			right=mid-1;
		}else{
			left=mid+1;
		} 
	}
	if(left<=n && a[left]==key){
		return left;
	}
	return -1;
}
int FindLastEqual(int key){
	int left=1;
	int right=n;
	while(left<=right){
		int mid=(left+right)/2;
		if(a[mid]<=key){
			left=mid+1;
		}else{
			right=mid-1;
		}
	}
	if(right>=1 && a[right]==key){
		return right;
	}
	return -1;
}
int FindLastNotMore(int key){
	int left=1;
	int right=n;
	while(left<=right){
		int mid=(left+right)/2;
		if(a[mid]>key){
			right=mid-1;
		}else{
			left=mid+1;
		}
	}
	return right;
}
int FindLastLess(int key){
	int left=1;
	int right=n;
	while(left<=right){
		int mid=(left+right)/2;
		if(a[mid]>=key){
			right=mid-1;
		}else{
			left=mid+1;
		}
	}
	return right;
} 
int FindFirstNotLess(int key){
	int left=1;
	int right=n;
	while(left<=right){
		int mid=(left+right)/2;
		if(a[mid]>=key){
			right=mid-1;
		}else{
			left=mid+1;
		}
	}
	return left;
}
int FindFirstMore(int key){
	int left=1;
	int right=n;
	while(left<=right){
		int mid=(left+right)/2;
		if(a[mid]>key){
			right=mid-1;
		}else{
			left=mid+1; 
		}
	}
	return left;
} 
void init(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	} 
}
int main(){
	freopen("a.txt","r",stdin);
//	a.txt 
//	15
//	1 1 2 5 5 5 5 5 5 6 7 8 9 11 13
	cout<<"15\n  1  1  2  5  5  5  5  5  5  6  7  8  9  11  13\n";
	cout<<"  1  2  3  4  5  6  7  8  9 10 11 12 13  14  15\n";
	init();
	int x=3;
	cout<<"FindFirstEqual(x)"<<FindFirstEqual(x)<<endl; 
	cout<<"FindLastEqual(x)"<<FindLastEqual(x)<<endl;
	cout<<"FindFirstMore(x)"<<FindFirstMore(x)<<endl;
	cout<<"FindFirstNotLess(x)"<<FindFirstNotLess(x)<<endl;
	cout<<"FindLastNotMore(x)"<<FindLastNotMore(x)<<endl;
	cout<<"FindLastLess(x)"<<FindLastLess(x)<<endl;
}

8 上面六种方法汇总

// 这里必须是 <=
while (left <= right) {
    int mid = (left + right) / 2;
    if (array[mid] ? key) {
        //... right = mid - 1;
    }
    else {
        // ... left = mid + 1;
    }
}
return xxx;
int xxx(int key){
	int left=1;
	int right=n;
	while(left<=right){
		int mid=(left+right)/2;
		if(a[mid]>=key){
			right=mid-1;
		}else{
			left=mid+1;
		}
	}
	...
	return xxx
}

三分问题

回到顶部

可以认为三分求导极值--二分零点

离散型

/\型

int SanFen(int l,int r) //找凸点  
{  
    while(l < r-1)  
    {  
        int mid  = (l+r)/2;  
        int mmid = (mid+r)/2;  
        if( f(mid) > f(mmid) )  
            r = mmid;  
        else  
            l = mid;  
    }  
    return f(l) > f(r) ? l : r; 
}  

\/型

int SanFen(int l,int r) //找凸点  
{  
    while(l < r-1)  
    {  
        int mid  = (l+r)/2;  
        int mmid = (mid+r)/2;  
        if( f(mid) > f(mmid) )  
            l = mid;  
        else  
            r = mmid;  
    }  
    return f(l) > f(r) ? l : r;  
}  

连续型

/\型

double three_devide(double low,double up)  
{  
    double m1,m2;  
    while(up-low>=eps)  
    {  
        m1=low+(up-low)/3;  
        m2=up-(up-low)/3;  
        if(f(m1)<=f(m2))  
            low=m1;  
        else  
            up=m2;  
    }  
    return (m1+m2)/2;  
}  

\/型

double three_devide(double low,double up)  
{  
    double m1,m2;  
    while(up-low>=eps)  
    {  
        m1=low+(up-low)/3;  
        m2=up-(up-low)/3;  
        if(f(m1)<=f(m2))  
            up=m2;  
        else  
            low=m1;  
    }   
    return (m1+m2)/2;  
}  

应用

image_1chv63h5f1pq3tm16uj1rgsqak1j.png-24.2kB

#include <iostream>
using namespace std;
double f(double x){
    return (0.1*x*x*x-x*x+2*x+1);
}
double sanfenshangtu(double left,double right){
    double m1,m2;
    double eps=1e-8;
    while(right-left>=eps){
        m1=left+(right-left)/3;
        m2=right-(right-left)/3;
        if(f(m1)>f(m2)){
            right=m2;
        }else{
            left=m1;
        }
    }
    return (left+right)/2;
}
double sanfenxiatu(double left,double right){
    double m1,m2;
    double eps=1e-8;
    while(right-left>=eps){
        m1=left+(right-left)/3;
        m2=right-(right-left)/3;
        if(f(m1)<f(m2)){
            right=m2;
        }else{
            left=m1;
        }
    }
    return (left+right)/2;
}
int main(){
    cout<<sanfenshangtu(0,10)<<endl<<f(sanfenshangtu(0,10))<<endl;
    cout<<sanfenxiatu(0,10)<<endl<<f(sanfenxiatu(0,10))<<endl;
}

image_1chv63h5f1pq3tm16uj1rgsqak1j.png-24.2kB

[Running] cd "o:\2\" && g++ sanfen.cpp -o sanfen && "o:\2\"sanfen
1.22515
2.1332
5.44152
-1.61468

[Done] exited with code=0 in 0.487 seconds
posted @ 2018-09-18 08:30  zsh2517  阅读(893)  评论(0编辑  收藏  举报