基础思路:答案是具有连续性的,它首先一定可以通过for循环一个个枚举出来判断是不是真正答案,在此基础上想要优化,才应该想到‘二分’这个算法。大多数时候,二分是复合在别的算法中出现的小优化。
二分的板子不算难抄,但是有很多随机应变的小问题:
1、二分L和R的边界问题。打个比方:洛谷P1182(数列分段 Section II),这道题中如果给出的数列是‘4 7 5 3 9’,那么无论怎么切是一定切不出来‘1’的。此时如果你的L边界为1,判断它的时候,程序遇到一个数字发现它大于1,就会把每个数字都切一下,切出来‘4’‘7’‘5’‘3’‘9’,但实际上并没有任何一个数确确实实等于‘1’了,(因为我们判断是根据大于了就切一刀的思路来判断的)。
2、二分过程中判断成立与否的check函数设计。二分通常会出现一些‘切割’‘增设节点’之类的问题,要注意到底切几刀、到底增几个,打个比方:洛谷P3853([TJOI2007] 路标设置),一段长为4的路,想要分成2的长度,要加几个路标?4/2是错误的,事实上只用放一个标就可以分成两个2了,这是小学数学经典的减一问题。
板子
while (l < r) { int mid = (l + r) / 2; (check函数设计) if (res > M) { l = mid + 1; } else { r = mid; }
这个是针对最大值最小的,当res==M的时候还可以找更小的,所以把R移到mid位置,继续往小去找。
最小值最大就是反过来,然后r=mid-1;
也有泛用的,看见挺多人用过,我自己也用过了(P2678 [NOIP 2015 提高组] 跳石头),记一下
while(l+1<r){ int mid=(l+r)/2; if(check(mid)) l=mid; else r=mid; }
也算泛用,但我没用过:
while(l<=r) { int mid=(l+r)/2; if(check(mid)) { ans=mid; r=mid-1; } else l=mid+1; }