浅谈二分的细节问题

发布时间 2023-09-21 11:05:38作者: 御坂夏铃

注意事项

一般 check 函数只能判断是否合法,没有等于这个概念。所以下面两个例子虽然可以通过改变判断符号进行相互转化,但实际问题是不行的。

最大值最小

给定一个不降的序列 \(a\),查找其中大于等于 \(x\) 的第一个数。

其实就是查找第一个合法的点。

while(l<r)
{
	mid=(l+r)>>1;
	if(a[mid]<x)l=mid+1;
	else r=mid;
}

如果当前 \(mid\) 小了,就向右寻找,当前 \(mid\) 不可能是答案,可以直接忽略,以 \(mid+1\) 为新的左端点。

如果当前 \(mid\) 大于等于了,就向左寻找,当前 \(mid\) 可能是答案,不可以直接忽略 \(mid\),应该以 \(mid\) 为新的右端点。

初始条件默认 \(l\leq r\)。因为是整除 \(\bm 2\),所以在循环中 \(mid\in[l,r),mid+1\in(l,r]\),不会死循环也不会跳出 \([l,r]\)

所以最后 \(l=r\),即为所求。

最小值最大

给定一个不降的序列 \(a\),求其中小于等于 \(x\) 的最后一个数。

其实就是查找最后一个合法的点。

while(l<r)
{
	mid=(l+r+1)>>1;
	if(a[mid]>x)r=mid-1;
	else l=mid;
}

如果当前 \(mid\) 大了,就向左寻找,当前 \(mid\) 不可能是答案,可以直接忽略 \(mid\),以 \(mid-1\) 为右端点。

如果当前 \(mid\) 小于等于了,就向右寻找,当前 \(mid\) 可能是答案,不可以直接忽略,应该以 \(mid\) 为新的左端点。

初始条件默认 \(l\leq r\)。因为是\(\bm 1\) 后整除 \(\bm 2\),所以在循环中 \(mid\in(l,r],mid-1\in[l,r)\),不会死循环也不会跳出 \([l,r]\)

所以最后 \(l=r\),即为所求。