题目来源:
第八届蓝桥杯省赛C++ A组/B组
第八届蓝桥杯省赛Java A组/B组/C组
在尝试解决这道题前,首先需要明确二分的概念,二分的目的是快速找出需要的那个值,正常顺序遍历所需要的时间复杂度是O(n),而二分的时间复杂度是O(logn)。二分的代码实现思路是用数据的中间索引来进行check函数判断,然后更新数组的左右边界,这样一直更新到数据中只有一个数据时,这时候就得到了我们想要的数据。那在什么情况下可以使用二分呢?现在说说我的理解:
假设题目给出了一组数据,而这组数据满足2个性质,可以把这组数据分成2半,注意这2个性质需要有明确的分界,比如说举一个数的二分的案例,题目要求找出需要找出数的索引,那么这组数据就必须要顺序或者逆序排序,所以说具有一定顺序的数据必然可以二分,但注意,不是二分必须要有顺序。
先给出2个整数二分模板,记住这两个模板,遇到整数二分题目直接套用,写个check函数即可:
模板代码:
1.整数二分(找右边性质的左边界)
l = 0
r = n - 1
while l < r:
mid = (l + r) // 2
if ls[mid] >= k: #条件判断写的是check函数
r = mid
else:
l = mid + 1
2.整数二分(找左边性质的右边界)
l = 0
r = n - 1
while l < r:
#这里需要+1进行向上取整操作,原因是假如l=0,r=1用原先的取整方法会导致check成功时l并不会更新,会出现死循环
mid = (l + r + 1) // 2
if ls[mid] <= k: #条件判断写的是check函数
l = mid
else:
r = mid - 1
浮点数二分就不用取整操作,也不需要考虑边界问题,所以也不存在mid+1,明确这个即可。梳理完模板后,下面我们来看一下分巧克力这道题目:
题目:
儿童节那天有 KK 位小朋友到小明家做客。
小明拿出了珍藏的巧克力招待小朋友们。
小明一共有 NN 块巧克力,其中第 ii 块是 Hi×WiHi×Wi 的方格组成的长方形。
为了公平起见,小明需要从这 NN 块巧克力中切出 KK 块巧克力分给小朋友们。
切出的巧克力需要满足:
- 形状是正方形,边长是整数
- 大小相同
例如一块 6×56×5 的巧克力可以切出 66 块 2×22×2 的巧克力或者 22 块 3×33×3 的巧克力。
当然小朋友们都希望得到的巧克力尽可能大,你能帮小明计算出最大的边长是多少么?
输入格式
第一行包含两个整数 NN 和 KK。
以下 NN 行每行包含两个整数 HiHi 和 WiWi。
输入保证每位小朋友至少能获得一块 1×11×1 的巧克力。
输出格式
输出切出的正方形巧克力最大可能的边长。
数据范围
1≤N,K≤1051≤N,K≤105,
1≤Hi,Wi≤1051≤Hi,Wi≤105
输入样例:
2 10
6 5
5 6
输出样例:
2
看到题目找出最大边长,我们可以很清晰的看出这道题可以采用二分方法,即找性质边界,这里的性质就是找够分给小朋友的最大边长或者不够分给小朋友的最小边长。然而这题不是数的二分,所以应该定义一个check函数,而check函数就是为了判断是否够分给小朋友。
题解:
1.二分够分给小朋友的最大边长
def check(x):
cnt=0
for h,w in query:
cnt+=(h//x)*(w//x)
return cnt >= k
n,k = map(int,input().split())
query = []
for _ in range(n):
h,w = map(int,input().split())
query.append([h,w])
l, r = 1, 100000
while l < r:
mid = (l + r + 1) // 2
#check现在这样分出来的巧克力个数能不能满足k个小朋友
if check(mid):
l = mid
else:
r = mid - 1
print(r)
2.二分不够分给小朋友的最小边长
n,k = map(int,input().split())
query = []
for _ in range(n):
h,w = map(int,input().split())
query.append([h,w])
def check(x):
cnt=0
for h,w in query:
cnt+=(h//x)*(w//x)
if cnt < k:
return True
else:
return False
l, r = 1, 100000 + 1
while l < r:
mid = (l + r ) // 2
if check(mid):
r = mid
else:
l = mid + 1
print(r - 1)
通过上面代码我们可以明确整个在做题过程中我们应该如何使用二分算法,本质上就是套用模板和书写check函数,但注意,选用左边的模板还是右边的模板还是要仔细斟酌一下,这样不仅书写方便,而且也容易理解。
最后给出一道二分的题目,小提示:运用了二分和差分,下一篇我们详细分析一下这个题目: