找出函数\(f(x) = 3x - 3\)在闭区间\([0,4]\)的零点。
在信息学中,二分法最常见的体现就是二分答案。
在这篇随笔中,我主要讲解的就是二分答案。
猜数字
事先准备一个\([1, 100]\)的正整数,让电脑猜。每次猜测会告诉猜数与答案的大小关系。
朴素的方法:枚举法,从\(1 - 100\)依次尝试,直到猜对为止。时间复杂度为\(O(n)\)。二分答案:\(L = 1, R = 100, mid = \lfloor \dfrac{L + R}{2} \rfloor = 50\),设答案为\(ans\)。 只要\(L \leqslant R\),尝试\(mid\),\[ \left\{ \begin{aligned} & 若mid > ans,则R = mid; \\ & 若mid < ans,则L = mid + 1; \\ & 若mid = ans,猜对了。 \end{aligned} \right.\] 时间复杂度为\(O(log n)\)。
什么意思呢?
我们不妨假设答案满足条件为\(1\),不满足为\(0\);
那么如果一个答案序列为\(0000000111\)或\(1111100000\),都存在单调性,那就都可以;
而如果序列是\(000011011000110000\),那就不满足单调性,于是就不能进行二分答案了。
二分答案能够解决哪些问题呢?如下:
最大的最小值最小的最大值在满足条件的情况下的最小(大)值最接近一个值的值…… 在一个单调序列中特殊的点基本上都能二分。为什么第五行要加上ans = mid呢?
原因:如果\(mid\)恰好就是正确答案,\(check(mid)\)满足,为\(true\),于是进入本句。如果将\(right\)设为\(mid - 1\)的话,就永远得不到\(ans\)了(可以想一想为什么)
这就出现了另一种写法——
// 这是一个求满足条件的最小值的二分模板 int left = 0, right = MAX, mid; // left为左边界,right为右边界 while(left < right){ // #注意这里改变# mid = (left + right) >> 1; // 等价于(left + right) / 2,只不过这样写运行速度会稍快一些 if(check(mid)) right = mid; // #注意这里也改变# else left = mid + 1; // 如果不能满足条件,ans区间最小值肯定大于mid } printf("%d\n", right); // 输出答案不过我个人建议还是写第一种好(更好理解,不容易错)。
那这两段代码中的\(check\)函数是干什么的呢?
其实,布尔型函数\(check(x)\)是用来判断\(x\)条件下是否能成功(满足题目条件)。
【模板】最长公共子序列
……
转载于:https://www.cnblogs.com/newbiePWang/p/BinarySearch.html