二分查找学习指南
前置芝士
查找最后一个<=q的数的下标
// l,r均初始化为开区间
int a[100010],n;
int find(int q){
int l=0,r=n+1;//左右指针为开区间
while(l+1<r){
int mid=l+r>>1;
if(a[mid]<=q) l=mid;
else r=mid;
}
return l;//l指针始终在可行解中跳转
}
查找第一个>=q的数的下标
int find(int q){
int l=0,r=n+1;
while(l+1<r){
int mid=l+r>>1;
if(a[mid]>=q) r=mid;
else l=mid;
}
return r;
}
求一个浮点数(-10000<=y<=10000)的三次方根
【最大化查找】
double find(double y){
double l=-100,r=100;
while(r-l>1e-5){
double mid=(l+r)/2;
if(mid*mid*mid<=y) l=mid;
else r=mid;
}
return l;
}
int main() {
double y;
scanf("%lf",&y);
printf("%.3lf\n",find(y));
return 0;
}
【最小化查找】
double find(double y){
double l=-100,r=100;
while(r-l>1e-5){
double mid=(l+r)/2;
if(mid*mid*mid>=y) r=mid;
else l=mid;
}
return r;
}
整数三分
【c++】
int l = 1,r = 100;
while(l < r) {
int lmid = l + (r - l) / 3; // l + 1/3区间大小
int rmid = r - (r - l) / 3; // r - 1/3区间大小
lans = cal(lmid),rans = cal(rmid);
// 求凹函数的极小值
if(lans <= rans) r = rmid - 1;
else l = lmid + 1;
// 求凸函数的极大值
if(lasn >= rans) l = lmid + 1;
else r = rmid - 1;
}
// 求凹函数的极小值
cout << min(lans,rans) << endl;
// 求凸函数的极大值
cout << max(lans,rans) << endl;
求函数凸点(整数三分)
使用两个点将之分为三段
midl = left + (right - left)/3;
midr= right - (right - left)/3;
- 如果midl比midr更加靠近最值点,我们就令 right = midr- 1; 【舍弃远离的那一段】
- 如果midr比midl更加靠近罪之颠,令left = midl + 1; 【舍弃远离的那一段】
int trisection_search(int[] arr){
if(arr==null){
return -1;
}
int left=0,right=arr.length-1;
while(left<=right){
int midl=left+(right-left)/3;
int midr=right-(right-left)/3;
if(arr[midl]==arr[midr]){
return midl;
}else if(arr[midl]>arr[midr]){
right=midr-1;
}else{
left=midl+1;
}
}
return -1;
}
浮点三分
const double EPS = 1e-9;
while(r - l >= EPS) {
double lmid = l + (r - l) / 3;
double rmid = r - (r - l) / 3;
lans = cal(lmid),rans = cal(rmid);
// 求凹函数的极小值
if(lans <= rans) r = rmid;
else l = lmid;
// 求凸函数的极大值
if(lans >= rans) l = lmid;
else r = rmid;
}
// 输出 l 或 r 都可
cout << l << endl;
打家劫舍IV
[problm description]
沿街有一排连续的房屋。每间房屋内都藏有一定的现金。现在有一位小偷计划从这些房屋中窃取现金。
由于相邻的房屋装有相互连通的防盗系统,所以小偷 不会窃取相邻的房屋 。
小偷的 窃取能力 定义为他在窃取过程中能从单间房屋中窃取的 最大金额 。
给你一个整数数组 nums
表示每间房屋存放的现金金额。形式上,从左起第 i
间房屋中放有 nums[i]
美元。
另给你一个整数 k
,表示窃贼将会窃取的 最少 房屋数。小偷总能窃取至少 k
间房屋。
返回小偷的 最小 窃取能力。
示例 1:
输入:nums = [2,3,5,9], k = 2 输出:5 解释: 小偷窃取至少 2 间房屋,共有 3 种方式: - 窃取下标 0 和 2 处的房屋,窃取能力为 max(nums[0], nums[2]) = 5 。 - 窃取下标 0 和 3 处的房屋,窃取能力为 max(nums[0], nums[3]) = 9 。 - 窃取下标 1 和 3 处的房屋,窃取能力为 max(nums[1], nums[3]) = 9 。 因此,返回 min(5, 9, 9) = 5 。
示例 2:
输入:nums = [2,7,9,3,1], k = 2 输出:2 解释:共有 7 种窃取方式。窃取能力最小的情况所对应的方式是窃取下标 0 和 4 处的房屋。返回 max(nums[0], nums[4]) = 2 。
提示:
n == stations.length
1 <= n <= 10^5
0 <= stations[i] <= 10^5
0 <= r <= n - 1
0 <= k <= 10^9
[solved]
看到「最大化最小值」或者「最小化最大值」就要想到二分答案,这是一个固定的套路。
为什么?一般来说,二分的值越大,越能/不能满足要求;二分的值越小,越不能/能满足要求,有单调性,可以二分。
设二分的最大金额为 mx,定义 f[i]表示在前 i 个房屋中窃取金额不超过mx 的房屋的最大个数。
分类讨论:
不选第 i个房屋:f[i]=f[i−1];
选第 i 个房屋,前提是金额不超过 mx:f[i]=f[i−2]+1。
这两取最大值,即
f[i]=max(f[i−1],f[i−2]+1)
代码实现时,可以用两个变量滚动计算。
最小化两个数组中的最大值
[problem description]
给你两个数组 arr1
和 arr2
,它们一开始都是空的。你需要往它们中添加正整数,使它们满足以下条件:
arr1
包含uniqueCnt1
个 互不相同 的正整数,每个整数都 不能 被divisor1
整除 。arr2
包含uniqueCnt2
个 互不相同 的正整数,每个整数都 不能 被divisor2
整除 。arr1
和arr2
中的元素 互不相同 。
给你 divisor1
,divisor2
,uniqueCnt1
和 uniqueCnt2
,请你返回两个数组中 最大元素 的 最小值 。
示例 1:
输入:divisor1 = 2, divisor2 = 7, uniqueCnt1 = 1, uniqueCnt2 = 3 输出:4 解释: 我们可以把前 4 个自然数划分到 arr1 和 arr2 中。 arr1 = [1] 和 arr2 = [2,3,4] 。 可以看出两个数组都满足条件。 最大值是 4 ,所以返回 4 。
示例 2:
输入:divisor1 = 3, divisor2 = 5, uniqueCnt1 = 2, uniqueCnt2 = 1 输出:3 解释: arr1 = [1,2] 和 arr2 = [3] 满足所有条件。 最大值是 3 ,所以返回 3 。
示例 3:
输入:divisor1 = 2, divisor2 = 4, uniqueCnt1 = 8, uniqueCnt2 = 2 输出:15 解释: 最终数组为 arr1 = [1,3,5,7,9,11,13,15] 和 arr2 = [2,6] 。 上述方案是满足所有条件的最优解。
提示:
2 <= divisor1, divisor2 <= 10^5
1 <= uniqueCnt1, uniqueCnt2 < 10^9
2<= uniqueCnt1 + uniqueCnt2 <= 10^9
[solved]
下文把 divisor1和 divisor2 简写成 d1 和 d2,记 LCM 为 d1 和 d2 的最小公倍数。
由于:
能被d2整除但不能被d1整除的数,能在arr1中且不能在arr2中。
能被d1整除但不能被d2整除的数,能在arr2中且不能在arr1中。
既不能被d1整除也不能被d2整除的数,可以在arr1和arr2中。
去掉独享的,剩余的数字只能在共享中选择,因此:
为二分判定条件。
代码实现时,二分上界可以取
因为最坏情况下 d1=d2=2只能取奇数。