二分答案的避坑指南
经过研究,本蒟蒻对令人懵逼的二分答案有了一点心得。
二分的思想主要分三种:
- l和r代表的“成本值”均可行,且有一个ans变量记录当前的最优
- l和r代表的“成本值”均可行,最后的答案是l或r
- l代表的“成本值”可行,r不可行,最后的答案是l
很多题目都可以转换为第一种思想。定义一个记录答案的变量即可。所以二分答案的难点还是落到了边界的处理和答案的更新上。什么时候用l,什么时候l和r均可?很多问题困扰了不少小白(包括笔者)。由于实力问题(害),这里只是笔者做题的一些避坑方法,帮助你少花时间调试和找到正解。
避坑1.要规避错误,一个要点是清楚什么情况是满足条件但不是最优解,另一种情况自然是不满足条件。在满足条件时要把答案更新,不满足则只更新边界。
避坑2.教练无数次举过一个例子left = 2,right = 3。此时需要一些操作,如令mid = (left + right + 1)/2,或left = mid + 1,或right = mid - 1;具体选择这三中的哪一个,依题而定。
上题:最长递增子序列
class Solution { public: int dp[5000] = {0}; int lengthOfLIS(vector<int>& nums) { int n = nums.size(); int len = 1; dp[1] = nums[0]; for(int i=1;i<n;i++) { if(dp[len] < nums[i]) dp[++len] = nums[i]; else { int l = 1,r = len; cout<<nums[i]<<" "<<len<<" "; while(l <= r) { int mid = (l + r) >> 1; if(dp[mid] < nums[i]) //不能等于o,最后找到一个小于nums[i]的数才是正解! 等于不是严格递增所以不属于正解 l = mid + 1; else r = mid - 1; } dp[l] = min(dp[l],nums[i]); cout<<l<<endl; } } return len; } };
当nums[i] 小于等于dp[len]时,我需要找到最大的小于等于nums[i] 的一个数dp[mid],那么必须让它后面的数(如果两数相等就是它本身)更新成nums[i],而L在while结束循环时一定是大于等于right的,因此答案是dp[l],不是dp[r]。

浙公网安备 33010602011771号