做法:
1.估计 最终答案可能的范围 是什么,可以定的很粗略,因为等下二分很快就排除了。
2.分析 问题的答案 和 给定的条件 之间的 单调性。
3.建立一个函数,当答案固定的时候,判断给定的条件是否达标。
4.在最终答案可能的范围上不断的二分搜索,每次使用函数判断,直到二分结束,找到最适合的答案
核心点:找到答案和条件之间的单调性
410. 分割数组的最大值 - 力扣(LeetCode)
思路:可以假设为有sum个墙面需要粉刷匠去刷,现在最多有k个粉刷匠,如果每个粉刷匠每天只能粉刷mid个墙面,求一天刷完墙的mid的最小值。随着二分法的进行,每次mid下最少需要的粉刷匠的值就可以使用函数f得到,如果数量比k大,则粉刷匠刷的墙比mid小的,就会完不成任务,则需要刷的墙面数量要比mid个多。同理小于的时候,就可以每个人再少刷几面墙,直到刚刚好k个粉刷匠完成整个任务。
//使用f函数,计算出在mid值下分组,需要最少的分组数 int f(vector<int>& nums, long limit) { int sum = 0; int parts = 1; for (int num : nums) { if (num > limit) { return INT_MAX; } if (sum + num > limit)//如果现在的和加上下一个大于limit,则已经满了. { parts++; sum = num; } else { sum += num; } } return parts; } int splitArray(vector<int>& nums, int k) { long sum = 0; for (int num : nums) { sum += num; } long left = 0; long right = sum; long mid = 0; long needk = 0;//实际需要的分组的个数 int ans = 0;//实际的分组数 while (left <=right) { mid = left + ((right - left) >> 1);//防止越界求中间值 needk = f(nums, mid); //大于时,则最少需要的分组数已经大小k了,证明mid左边都不可以满足k的分组数 if (needk > k) { left = mid + 1; } else//小于等于的时候,证明当前分组数比k更小,就可以再分的小一点,则mid的左边有更优解 { right = mid - 1; ans = mid; } } return (int)ans; } };
875. 爱吃香蕉的珂珂 - 力扣(LeetCode)
思路:显然知道猴子吃香蕉的时间来找每小时吃的数量很难做到,转换思路为通过锁定每小时吃的数量来对应时间,正好随着数量的变化时间也随之单调变化。猴子每小时吃的最少为1个,最多就是数组元素里最大的香蕉堆数,因为再大它吃完只会等待1小时结束。通过每小时吃的数量就可以确定需要吃完这些香蕉的天数,再比对h找到最优解即可。
class Solution { public: long f(vector<int> piles, long limitspeed) { long time = 0; for (int pile : piles) { time += (pile + limitspeed - 1) / limitspeed;//向上取整 } return time; } int minEatingSpeed(vector<int>& piles, int h) { //long right=maxpile(piles);//计算数组里最多的香蕉数 int right = 0; for (int pile : piles) { right = max(pile, right);//向上取整 } long left = 1; long mid = 0; int ans = -1; long needk = 0; while (left <= right) { mid = left + ((right - left) >> 1); needk = f(piles, mid);//计算在mid的速度下,吃完需要多久 if (needk > (long)h)//该速度下,需要的时间大于h,说明速度太小了,所以mid前面的值都不可取,给右侧二分 { left = mid + 1; } else if (needk <= (long)h)//该速度下,需要的时间小于h,说明mid的右边都可以满足,所以给mid左侧二分找更小速度 { ans = mid; right = mid - 1; } } return ans; } };
719. 找出第 K 小的数对距离 - 力扣(LeetCode)
思路:找第k小的数组对距离,因为当数组很大的时候,对数组差进行排序显然效率很低,转换思路,找数组差为x的时候,有多少对数组满足小于k对的,当x的值变化时,数组对数也单调变化。数组对差值的有范围就是最大值-最小值,随着差值的变化,找到刚好满足k个数组的解,就是最优解。
class Solution { public: int f(vector<int>nums, int limit) { int realk = 0; for (int i = 0, r = 0; i < nums.size(); i++) { while (r + 1 < nums.size() && nums[r + 1] - nums[i] <= limit) { r++; } realk += r - i; } return realk; } int smallestDistancePair(vector<int>& nums, int k) { sort(nums.begin(), nums.end());//sort函数,对数组进行排序 int left = 0; int right = nums[nums.size() - 1]; int mid = 0; int ans = -1; int realk = 0; while (left <= right) { mid = left + (right - left) / 2; realk = f(nums, mid);//计算mid为差值时,有多少个数组满足差小于mid if (realk < k)//两数差小于mid的对数小于k对,说明两数差值太小了,所以mid左边都不可能满足大于k { left = mid + 1; } else if (realk >= k)//两数差大于mid的对数比k多,说明mid的差太大了很多都满足,所以mid的右边肯定都满足 { right = mid - 1; ans = mid; } } return ans; } };