ENFP-T型|

_Youngxy

园龄:3年7个月粉丝:5关注:28

二分与三分

二分

二分查找

前提:数据有序 and 连续空间

复杂度只有O(logn)

非常优秀的算法!

img
code

#incldue<bits/stdc++.h>
#define ll long long
using namespace std;
ll a[10002],n,x;
int main(){
    scanf("%lld%lld",&n,&x);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
    }
    ll l=1,r=n,ans=-1;
    while(l<=r){
        int mid=l+(r-l)/2;//防止溢出
        if(a[mid]==x){
            ans=mid;
             break;
        }
        if(x<a[mid]) r=mid-1;
        else l=mid+1;
    }
    printf("%lld",ans);
    return 0;
}

易错点:

  1. while循环条件:l<=r
  2. 变化:r=mid-1| l=mid+1

查找x第一次出现的位置

ll erfe1(ll x) {
    l=1,r=n,ans=-1;
    while(l<=r) {
        mid=l+(r-l)/2;
        if(a[mid]==x) {
            ans=mid;
            r=mid-1;//找到了,也要往前找
        }else if(a[mid]>x){
            r=mid-1;
        }else l=mid+1;
    }
    if(ans==-1) return -1;
    return ans+1;
}

查找x最后一次出现的位置

ll erfe2(ll x){
    l=1,r=n,ans=-1;
    while(l<=r){
        mid=l+(r-l)/2;
        if(a[mid]==x){
            ans=mid;
            l=mid+1;
        }else if(a[mid]>x){
            r=mid-1;
        }else l=mid+1;
    }
    if(ans==-1) return -1;
    return ans+1;
}

查找a[i] (≤x)的最小值

ll erfe3(ll x){
    ll ans=,l=1,r=n; 
    while(l<=r){ 
        ll mid=l+(r-l)/2; 
        if(a[mid]>=x) {
            ans=a[mid];
            r=mid-1;
        } else l=mid+1; 
    }
    return ans;
}

查找a[i] (≥x)的最小值

ll erfe4(ll x){
    ll ans=-1,l=1,r=n,fg=-1; 
    while(l<=r){ 
        ll mid=l+(r-l)/2; 
        if(a[mid]>=x) {
            ans=a[mid];
            fg=mid-1;
            r=mid-1;
        } else l=mid+1; 
    }
    return a[fg];
}

求a[i] (=x)的值的个数
个数=x第一次出现的位置-x最后一次出现的位置
code

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,m;
ll q,ans;
ll a[10000002];
ll l,r,mid;
ll erfe1(ll q) {
    l=1,r=n,ans=-1;
    while(l<=r) {
        mid=l+(r-l)/2;
        if(a[mid]==q) {
            ans=mid;
            r=mid-1;
        }else if(a[mid]>q){
            r=mid-1;
        }else l=mid+1;
    }
    if(ans==-1) return -1;
    return ans+1;
}
ll erfe2(ll q){
    l=1,r=n,ans=-1;
    while(l<=r){
        mid=l+(r-l)/2;
        if(a[mid]==q){
            ans=mid;
            l=mid+1;
        }else if(a[mid]>q){
            r=mid-1;
        }else l=mid+1;
    }
    if(ans==-1) return -1;
    return ans+1;
}
int main() {
    scanf("%lld%lld",&n,&m);
    for(ll i=1; i<=n; i++) {
        scanf("%lld",&a[i]);
    }
    while(m--) {
        scanf("%lld",&q);
        if(erfe1(q)==-1) printf("0 ");
        else printf("%lld ",erfe2(q)-erfe1(q)+1);
    }
    return 0;
}

实数域上二分查找

通俗易懂一点就是double二分

但是

不能直接套代码!!!

精度问题

在计算机中,小数点表示是不准确的
在传统二分中,边界条件 l≤r 会造成小数精度上的误差
所以我们使用 l+eps<r 作为边界条件
const double eps=0.001(精度要求保留一位小数)

double l=1,r=n; 
while(l+eps<r){ 
    double mid=(l+r)/2; 
    if(a[mid]>x) r=mid; 
    else l=mid; 
}//最后的答案为l或r

为什么精度要求1位但是eps =0.001?

保留k位小数,eps==10^{-k}:第k位有误差

例:保留2位小数,则结果可能6.23 也可能6.24

一般来说精度+2位避免误差。

三分

有一类函数被称为单峰函数,它们拥有唯一的极大值和极小值,如下图。
img
以最大值为例。在最大值左侧严格单调递增,右侧严格单调递减。可以使用三分法求极值。
以存在唯一极大值的单峰函数 f 为例,在 [l,r] 找到最大值。在范围内任取两个点 lmid,rmid,将函数分为三段。

  1. 若 f(lmid)<f(rmid) ,则 lmid,rmid 要么同时处于极大值左侧,或者处于极大值两侧。无论哪种情况,极大值都在 lmidlmid 的右侧,令 l=lmid 。
  2. 若 f(lmid)>f(rmid) ,则极大值点一定在 rmidrmid 左侧,令r=rmid 。

如果取 lmid 和 rmid 为三等分点,那么定义的范围每次缩小 13 。通过在 log 级别的时间复杂度下,即可求出指定精度的极值。称为三分法。也可以取lmid 和 rmid 在二等分点两侧接近的地方,那么定义的范围每次缩小 12 。
需要注意,单峰函数必须严格单调。若函数中存在一段值相等的部分,无法判断左右边界如何缩小,三分法就不适用了。

【例2】三分法

题目描述
给出一个 N 次函数
保证在范围 [l,r] 内存在一点 x
使得 [l,x] 上单调增,[x,r] 上单调减
试求出 x 的值
输入
第一行一次包含一个正整数 N(1≤N≤13)$和两个实数 l,r,含义如题目描述所示。
第二行包含 N+1 个实数,从高到低依次表示该 N 次函数各项的系数。
输出
输出为一行,包含一个实数,即为 x 的值。四舍五入保留 5 位小数。
样例输入1

3 -0.9981 0.5
1 -3 -3 1

样例输出1

-0.41421

【参考程序】

#include<bits/stdc++.h>
#define dol double 
using namespace std;
int n;
dol l,r;
dol a[15];
dol f(dol x) {
	dol sum=0;
	for(int i=n; i>=0; i--){
		sum=sum*x+a[i];
	}
	return sum;
}//函数 
int main() {
	ios::sync_with_stdio(false);
	cin>>n>>l>>r;
	for(int i=n; i>=0; i--){
		cin>>a[i];
	}//注意:题目中说的是从高到低 所以要倒过来 
	dol eps=1e-7;
	while(l+eps<r) {
		dol t=(r-l)/3;    //三分
		dol lmid=l+t,rmid=r-t;
		if(f(lmid)<f(rmid)) l=lmid;
		else r=rmid;
	}
	printf("%.5lf",l);
	return 0;
}

完结撒花❀

★,°:.☆( ̄▽ ̄)/$:.°★

本文作者:Yvette的博客

本文链接:https://www.cnblogs.com/yvette1217/p/15936870.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   _Youngxy  阅读(99)  评论(0编辑  收藏  举报
评论
收藏
关注
推荐
深色
回顶
收起
点击右上角即可分享
微信分享提示