labuladong讲解
代码框架
// 函数 f 是关于自变量 x 的单调函数 int f(int x) { // ... } // 主函数,在 f(x) == target 的约束下求 x 的最值 int solution(int[] nums, int target) { if (nums.length == 0) return -1; // 问自己:自变量 x 的最小值是多少? int left = ...; // 问自己:自变量 x 的最大值是多少? int right = ...; while (left <= right) { int mid = left + (right - left) / 2; if (f(mid) == target) { // 问自己:题目是求左边界还是右边界? // ... } else if (f(mid) < target) { // 问自己:怎么让 f(x) 大一点? // ... } else if (f(mid) > target) { // 问自己:怎么让 f(x) 小一点? // ... } } return left; }
875. 爱吃香蕉的珂珂(中等)
题目:
思路:
珂珂每小时最多只能吃一堆香蕉,如果吃不完的话留到下一小时再吃;如果吃完了这一堆还有胃口,也只会等到下一小时才会吃下一堆。
他想在警卫回来之前吃完所有香蕉,让我们确定吃香蕉的最小速度K
f函数为单调递减,我们要求左侧边界
class Solution { public: int minEatingSpeed(vector<int>& piles, int h) { //最小速度1 int left=1; //最大速度10^9 int right=1000000000; while(left<=right){ int mid=left+(right-left)/2; //当f<h时,增大f,需要减小k,也就是mid要减小,缩短右边界 if(f(piles,mid)<h) right=mid-1; else if(f(piles,mid)>h)//当f>h时,减小f,需要增大k,也就是mid要增加,左边界增大 left=mid+1; else right=mid-1;//f==h时,我们求k最小值,也就是最左侧,所以缩小右边界 } return left; } //单调递减函数,K吃的速度越大,耗时越短 int f(vector<int>& piles, int k){ int h=0; for(int i=0;i<piles.size();++i){ //吃完一堆的耗时,不满K按1小时算 h+=piles[i]/k; if(piles[i]%k>0){ h++; } } return h; } };
1011. 在D天内送达包裹的能力(中等)
题目:
思路:
要在D
天内按顺序运输完所有货物,货物不可分割,如何确定运输的最小载重
f函数为单调递减函数,target
显然就是运输天数D
,我们要在f(x) == D
的约束下,算出船的最小载重
船的最小载重应该是weights
数组中元素的最大值,因为每次至少得装一件货物走,不能说装不下嘛。
最大载重显然就是weights
数组所有元素之和,也就是一次把所有货物都装走
现在我们确定了自变量x
是船的载重能力,f(x)
是单调递减的函数,target
就是运输总天数限制D
,题目要我们计算船的最小载重,也就是x
要尽可能小
这就是搜索左侧边界的二分搜索
class Solution { public: int shipWithinDays(vector<int>& weights, int days) { int left=0,right=0; for(int i=0;i<weights.size();++i){ left=max(left,weights[i]); right+=weights[i]; } while(left<=right){ int mid=left+(right-left)/2; if(f(weights,mid)>days) left=mid+1; else if(f(weights,mid)<days) right=mid-1; else right=mid-1; } return left; } // 定义:当运载能力为 x 时,需要 f(x) 天运完所有货物 // f(x) 随着 x 的增加单调递减 int f(vector<int>& weights, int d){ int days=0; for(int i=0;i<weights.size();){ //顺序累加重量直到超量 int w=0; while(i<weights.size()){ w+=weights[i]; if(w>d) break; i++; } days++; } return days; } };