模板 - 二分

之前一直都没有想清楚整数的二分到底是要打算怎么搞。

首先约定二分的区间为 $[l,r]$ 闭区间。

看一下下面这个实现,由于我们的约定,所以l与r都要取能取到的(合法的)值。

#include<bits/stdc++.h>
using namespace std;

int n;

//找大于等于n的最小的m
bool ok(int m){
    return m>=n;
}

int main(){
    while(~scanf("%d",&n)){
        int l=1,r=1000000,m,ans;
        while(1){
            m=(l+r)>>1;
            printf("%d\n",m);
            if(m==l){
                //边界情况
                if(ok(l)){
                    //假设要求最小值,要先判断l
                    ans=l;
                }
                else if(ok(r)){
                    //l不行再判断r,不能直接返回,因为不能确定答案是l或r其中之一
                    ans=r;
                }
                else{
                    ans=-1;
                }
                break;
            }
            if(ok(m)){
                //假设要求最小值,m满足条件则去找更小的
                r=m;
            }
            else{
                //m不满足条件,排除掉
                l=m+1;
            }
        }
        printf("ans=%d\n",ans);
    }
}

之前为什么会觉得边界条件是 $l$ 和 $r$ 相差为1,其实边界条件是 $l$ 和 $r$ 重合。其上一步的条件是 $l$ 与 $m$ 重合而 $l$ 和 $r$ 相差为1,此时若 $m$ 满足条件则 $r=m$ 收缩至 $l$ ,否则 $l=m+1$ 扩大至 $r$ 。所以这才是正确的写法。但是上面这样写完全足够了。下面是假设答案一定存在在 $[l,r]$ 中任意一个整数中。

#include<bits/stdc++.h>
using namespace std;

int n;

//找大于等于n的最小的m
bool ok(int m){
    return m>=n;
}

int main(){
    while(~scanf("%d",&n)){
        int l=1,r=1000000,m;
        while(l<r){
            m=(l+r)>>1;
            printf("%d\n",m);
            if(ok(m)){
                //假设要求最小值,m满足条件则去找更小的
                r=m;
            }
            else{
                //m不满足条件,排除掉
                l=m+1;
            }
        }
        printf("ans=%d\n",l);
    }
}

 

posted @ 2019-02-27 00:42  韵意  阅读(112)  评论(0编辑  收藏  举报