二分的细节

  满足单调性的情况下,可以用二分法找出答案,复杂度为logn

  1.在整数范围内,闭区间[l,r]的二分,以l==r为结束条件。边界条件需要考虑几个问题,当l或r更新的时候范围是否有缩小(若无则选入死循环),是赋得新值之后l是否==r而不是>r;

  2.根据题目设计选取mid归属于左半区间还是右半区间,假设我们要在单调递增的数组里面找第一个 >=val的值  假设a[mid]符合条件,那么可知mid的右边全都符合条件,但不知道mid的左边有没有符合条件的数字,因此在划分区间的时候不可以将mid去掉,因此r=mid,l=mid+1;为了使r=l+1的时候l<=r,此时mid=(l+r)>>1==l;

1 int binary_1(int l,int r,int val)
2 {
3     while(l<r)
4     {
5         int mid=(l+r)>>1;
6         if(a[mid]>=val)    r=mid;else l=mid+1;
7     }
8     return l;
9 }

  3.假设还是一个单调递增的数列,取第一个<=val的值,假如a[mid]符合条件,则mid的左半部分都符合条件,右半部分不一定有符合条件的,因此不能舍去,l=mid,r=mid-1;mid=(l+r+1)>>1;当r=l+1时,mid=r,为了缩小边界,r=mid-1;

int binary_2(int l,int r,int val)
{
    while(l<r)
    {
        int mid=(l+r+1)>>1;
        if(a[mid]<=val)    r=mid-1;else l=mid;
    }
    return l;
}

    其中用位运算>>1而不是用/2 是因为在遇到负数的时候>>1是向下取整, /2是向0取整

  4.可以把[1,n]扩大为[0,n] [1,n+1],当去到r取0,或者l取n+1的时候即无解

  5.在实数域上的二分只需要考虑精度,则确定精确度即可,抑或是二分固定次数

  

 1 double binary(double l,double r,double exp)
 2 {
 3     while(r-l>exp)
 4     {
 5         double mid=(l+r)/2;
 6         if(cal(mid)) l=mid;else r=mid;
 7     }
 8
 9     for(int i=1;i<=100;i++)
10     {
11         double mid=(l+r)/2;
12         if(cal(mid))l=mid;else r=mid;
13     }
14 }

  6.当l+r很大的时候有可能溢出,更妥当的写法是 l+(r-l)>>1     /    l+(r-l)>>1+1;

  7.三分法求极值(单峰函数严格单调) 将区间三分,比较mid1和 mid2 极值点一定不在某个区间上,此区间为[l,mid1]或[mid2,r]

  

double maxx(double l,double r,double exp)
{
    while(r-l>exp)
    {
        double k=r-l/3,mid1=l+k,mid2=r-k;
        if(f(mid1)<f(mid2))    l=mid1;else r=mid2;
    }
    return l;
}

  

原文地址:https://www.cnblogs.com/smoncaff/p/12178531.html

时间: 2024-08-11 11:45:13

二分的细节的相关文章

区间质数

[题目描述] 区间质数个数. [输入描述] 一行两个整数:询问次数n,范围m. 接下来n行,每行两个整数:[l,r]表示区间. [输出描述] 对于每次询问输出个数t,如l或r∉[1,m]输出:Crossing the line. [输入样例] 2 5 1 3 2 6 [输出样例] 2 Crossing the line [数据范围及提示] 对于20%的数据:1<=n<=10,1<=m<=10. 对于100%的数据:1<=n<=1000,1<=m<=10000

【NOIP2015】提高组

因为一些事情补了三天终于补完辣>< DAY1 T1神奇的幻方 题目链接 超级水的模拟题......一次过 #include<cstdio> #include<cstring> #include<iostream> using namespace std; int map[40][40]={0}; int main() { int n,head[2]; scanf("%d",&n); map[1][n/2+1]=1; head[0]

[JSOI2018]列队

题解 好像是\(JSOI2018\)最简单的一道题了,但是我还是做了好久== 所有人都往一个区间走可以转化为把编号为\([l,r]\)的人按照开始位置排序,然后排名为i的人走到\(k+i-1\)的位置的花费和 这样就是\(O(nmlogn)\)的了 那考虑用数据结构来优化这个过程 首先想到能不能用所有人的位置和-\((k+k+r-l+1)*(r-l+1)/2\) 显然不能,因为有些人会往左走,有些人会往右走 但我们可以发现在\([l,r]\)中一定有一个分界点,使得这个分界点往左的人都往右走,这

记OI退役

前言 (这篇本来在联赛前写了一点,但是一直没有发布.现在退役了,还是把它发出来留作纪念吧!) 其实,这篇随笔早该在停课时就写,可是我却迟迟没有动笔. 可能是我真的太懒了,或许也是我想要逃避自己内心的真实想法. 我看不清联赛以后的路,因此还是决定写下自己内心深处的回忆以及想法,也算是提醒自己吧. 正文 严格来说,我是一个OI生涯足足有五年的Oier,今年是我接触OI以来的第六年. 似乎在我前几年的OI生涯中就没有顺风顺水过. 小学.初一相当于在打酱油,在学校一年甚至学不完基础的C语言, 初二好不容

C++ - 二分查找完整版(包括注意细节)

  int getPos(vector<int> A, int n, int val)               int left = 0; int right = n-1;     int index = n;   while(left <= right) { int mid = (left+right)/2; if(A[mid] == val)         {             if(mid < index)    index = mid;             

二分细节处理(没有任何人去关注,但是确实让我很伤脑,处理不好会死人吧!)

以下内容大部分为转载 把二分查找算法写正确需要注意的地方 今天再次解决一个需要使用二分查找的问题,再一次的,我又没有一次过写对.(为什么我说"又"?) 抓狂了,似乎开始有一些"二分查找恐惧症". 为了以后能够一次将这个基本的算法写对,我决定再仔细研究一下.我之前有写过一个二分查找的算法,在这里,这一次再以这个问题为例来说明. 我今早写下的错误代码类似于下面的样子: #include <stdio.h> int search(int array[], in

小A与最大子段和 斜率优化 + 二分 + 细节

Code: #include <bits/stdc++.h> #define setIO(s) freopen(s".in","r",stdin) #define x(i) (1.0000*i) #define y(i) (1.0000*s2[i]*i-1.00000*s1[i]) #define maxn 300000 #define ll long long #define ldb double using namespace std; int ta

hdu_5884_Sort(二分+单调队列)

题目链接:hdu_5884_Sort 题意: 有n个数,每个数有个值,现在你可以选择每次K个数合并,合并的消耗为这K个数的权值和,问在合并为只有1个数的时候,总消耗不超过T的情况下,最小的K是多少 题解: 首先要选满足条件的最小K,肯定会想到二分. 然后是如何来写这个check函数的问题 我们要贪心做到使消耗最小,首先我们将所有的数排序 然后对于每次的check的mid都取最小的mid个数来合并,然后把新产生的数扔进优先队列,直到最后只剩一个数. 不过这样的做法是n*(logn)2 ,常数写的小

二分小结

NOIP中二分应该是很简单的算法了,去年noip的day2-t1就是裸的二分,这里有两个例题 1.poj2456:Farmer John has built a new long barn, with N (2 <= N <= 100,000) stalls. The stalls are located along a straight line at positions x1,...,xN (0 <= xi <= 1,000,000,000). His C (2 <= C