@TODO;

算法 {二分,浮点数二分,三分}

二分

錯誤

即使二分區間不溢出, 但運算依然可能會溢出;
比如int l = -2e9, r = 2e9, 假如答案是-2e9 那麽最後一次就是l=r=-2e9 此時mid = (l+r)/2 會導致l+r=-4e9溢出了; 同樣假如答案是2e9 也是l+r=4e9溢出了; 當然 假如答案是0 那麽此時不會溢出;
因此 二分的合法區間 必須要取最大值的一半, 即int l = -1e9, r = 1e9 這是沒問題的;

浮点数二分

性质

Finally, l ≤ a n s ≤ r l \leq ans \leq r lansr: 1 if the question asked 下取整, you should use r r r; 2 if 上取整, you should use l l l;
. e.g., 102. 最佳牛围栏

算法

模板代码

{ // 浮点数二分
    auto __check_success = [&]( Double _mid) -> bool{
        ?
    };
#define  __ANSWER_IN_LEFT_  ?  // 如果`1` 对应为`l=mid`, 否则要定义为`0` 即`r=mid`;
    Double __l = ?, __r = ?;
    ASSERT_( ___SupimoTools::Double_cmp( __l, __r) == -1);
    Double __ANS;
    while( __r - __l >= Double(1e-?)){  // 比如題目要求精度為`1e-9` 那麼這裡就設置為`1e-9`;
        __ANS = (__l + __r) / 2;
        if( __check_success( __ANS)){
#if  __ANSWER_IN_LEFT_
            __l = __ANS;
#else
            __r = __ANS;
#endif
        }
        else{
#if  __ANSWER_IN_LEFT_
            __r = __ANS;
#else
            __l = __ANS;
#endif
        }
    }
#if  __ANSWER_IN_LEFT_
    __ANS = __l;
#else
    __ANS = __r;
#endif
    if( __check_success( __ANS)){ // `__ANS`为答案
        ?
    }
    else{
        ?
    }
#undef  __ANSWER_IN_LEFT_    
} // 浮点数二分

例题

LINK: https://www.acwing.com/problem/content/submission/5051/;
如果设置成1e-8的精度 会进入死循环, 原因是: LINK: (https://editor.csdn.net/md/?articleId=128991752)-(@LOC_0);
. 即虽然a<b, 但是(a+b)/2并不是>a && <b的, 而是会等于a/b; 此时, 就掉入死循环了;
解决办法是:
. 要么: 使用double 精度设置成Tools::__DoubleEpsilon_ = 1e-6 (因为题目就是说精度是1e-6, 那你就把让while( r - l >= 1e-6)即可; 你设置那么高的精度干嘛…)
. 要么: 使用long double, 此时精度可以设置成1e-8;
. 参见: LINK: (https://editor.csdn.net/md/?not_checkout=1&articleId=128991752)-(@LOC_1);

整数二分

算法

模板代码

{ // 整数二分
    auto __check_success = []( long long _mid) -> bool{
        @TODO;
    };
#define  __ANSWER_IN_LEFT_  @TODO // 如果`1` 对应为`l=mid`, 否则要定义为`0` 即`r=mid`;
    long long __l = @TODO, __r = @TODO;
    ASSERT_( __l <= __r);
    long long __mid;
    while( __l < __r){
        __mid = (__l + __r + __ANSWER_IN_LEFT_) >> 1;
        if( __check_success( __mid)){
#if  __ANSWER_IN_LEFT_
            __l = __mid;
#else
            __r = __mid;
#endif
        }else{
#if  __ANSWER_IN_LEFT_
            __r = __mid - 1;
#else
            __l = __mid + 1;
#endif
        }
    }
    //>< `l == r`;
    if( __check_success( __l)){
        @TODO;
    }
    else{
        @TODO;
    }
#undef  __ANSWER_IN_LEFT_
} // 整数二分

性质

最终的布尔二段式 比如是: calc( < ans) < K; calc( >= ans) >= K
那么, 你的calc( x)函数, 假如他返回值是[0, 1e9], 那么 你不必要非得 让他按照定义的来,
比如, calc函数的执行过程是: 一个个累加, 那么, 返回值是1e9 这肯定是超时的…
而假如K只有1e5, 那么, calc函数 返回一个1e9, 和 返回一个1e5(K), 是完全等价的!
因为, 我们并不关心, 他返回值具体是多少, 只是判断 到底是(< K) 还是 (>= K)
因此, 在calc函数里, 一旦累加到K, 就可以返回了!
具体例题:

错误

#求满足条件的最小/最大值, 不一定就是二分;#

比如他是[x, x, x, x, v, x, v, x, v, x], 第一个v 他是4 即答案是4; 但他不符合二分;

@DELI;

succ = false;
while( l < r){
    int mid = (l + r) >> 1;
    if( Check( mid)){
    	succ = true;
        r = mid;
    }
    else{
        l = mid + 1;
    }
}

if( false == succ){
	cout<< "No Solution";
	return;
}
cout<< r;

Above code is wrong, the answer (i.e., r) maybe not detected in the process while( l < r){}.
For example, l = 1, r = 2 (the answer is 2), so mid = 1, we get check( 1) = false so l = r = 2, then the process while(){} has terminated; however, we have not test check( 2);

So, the standard code is

while( l < r){
    int mid = (l + r) >> 1;
    if( Check( mid)){
        r = mid;
    }
    else{
        l = mid + 1;
    }
}

if( false == Check( r)){
	cout<< "No Solution";
	return;
}
cout<< r;

@Delimiter

三分

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值