蓝桥杯——二分专题

二分分为:实数二分,二分理论题

 二分套路题:最小值最大化,最大值最小化

运用二分满足条件:有界,单调。

1.两个二分模板

找>=x的第一个,mid=(low+high)//2 ,没有就找比他大的下一个数

a=[0,3,5,7,9,11,13]
# [0,1,2,3,4,5,6]
# low = 0 high =6 mid =3  ----->   low=4  high =6
# low = 4 high =6 mid =5  ----->   low=4  high =5
# low = 4 high =5 mid =4  ----->   low=5  high =5
# break   low=high=5
# 找的是靠右的那一个
low=0
high=len(a)-1
def search(low,high,x):  # 查找的是后一个
    while (low<high):
        mid =(low+high)//2   # (2+3)//2=2  偏左
        if (a[mid]>=x):
            high=mid
        else:
            low=mid+1
    print(a[low])
    print(a[high])
search(low,high,10)  # 查找结果10

 找<=x的第一个,mid=(low+high+1)//2 ,没有就找比他小的前一个数

a=[0,3,5,7,9,11,13]
# [0,1,2,3,4,5,6]
# low = 0 high =6 mid =3  ----->   low=3  high =6
# low = 3 high =6 mid =5  ----->   low=3  high =4
# low = 3 high =4 mid =4  ----->   low=4  high =4
# break   low=high=4
# 找的是靠左的那一个
low=0
high=len(a)-1
def search(low,high,x):  # 查找的是前一个
    while (low<high):
        mid =(low+high+1)//2   # (2+3+1)//2=3  偏右
        if (a[mid]<=x):
            low=mid
        else:
            high=mid-1
    print(a[low])
    print(a[high])
search(low,high,10)  # 查找结果10
 

二分例题

1.分巧克力(根据题意转为二分,二分找边长N)

import os
import sys
 
# 请在此输入您的代码
def check(d):  # 检查蛋糕大小为d是否可分
  num = 0
  for i in range(n):
    num+=(w[i]//d)*(h[i]//d)
  if(num>=k):return True
  else:return False
 
h = [0]*100010
w = [0]*100010
n,k = map(int,input().split())
for i in range(n):  # 读入蛋糕大小
  h[i],w[i] = map(int,input().split())
 
L,R = 1,100010   # 结尾更大防止出现边界问题
while L<R:
   mid=(L+R+1)//2   # 偶数中值为右值 [1,2] -->2  ,没有则取前
   if(check(mid)):
     L=mid
   else:
     R=mid-1
 print(L)
 
#  mid=(L+R)//2    #偶数中值为左值  [1,2] -->1  ,没有则取后
#  if(check(mid)):
#    L=mid+1
#  else:
#    R=mid
#print(L-1)

更合理的写法

import sys  #设置递归深度
import collections  #队列
import itertools  # 排列组合
import heapq  #小顶堆
import math
sys.setrecursionlimit(300000)
 
n,k = map(int,input().split())
save = [] #存放巧克力
Max=1
for i in range(n):
   temp=list(map(int,input().split()))
   Max=max(max(temp),Max)  # 记录大巧克力边长,之后用于二分
   save.append(temp)
 
 
def check(i):
   count=0
   for a,b in save:
      count+=(a//i)*(b//i)
   if count>=k:
      return True
   else:
      return False
left=1
right=Max
while(left<right):  # 找<=x的第一个
   
   mid=(left+right+1)//2
   #print(left,mid,right)
   #print('-'*40)
   if check(mid):
      left=mid
   else:
      right=mid-1
print(left)
#print(right)
#print(mid)
 
 
'''
2 10
6 5
5 6
测试结果
1 4 6
----------------------------------------
1 2 3
----------------------------------------
2 3 3
----------------------------------------
2
2
3
'''
 

2.跳石头 (根据题意转为二分,二分找d)

 开始在i(i=0)位置,我在跳下一步的时候去判断我这个当前跳跃的距离,如果这个跳跃距离比二分出来的mid小,那这就是一个不合法的石头,应该移走。为什么?我们二分的是最短跳跃距离,已经是最短了,如果跳跃距离比最短更短岂不是显然不合法,是这样的吧。移走之后要怎么做?先把计数器加上1,再考虑向前跳啊。去看移走之后的下一块石头,再次判断跳过去的距离,如果这次的跳跃距离比最短的长,那么这样跳是完全可以的,我们就跳过去,继续判断,如果跳过去的距离不合法就再拿走,这样不断进行这个操作,直到i = n+1,为啥是n+1?河中间有n块石头,显然终点在n+1处。(这里千万要注意不要把n认为是终点,实际上从n还要跳一步才能到终点)。

import sys
import collections
import itertools
import heapq
sys.setrecursionlimit(300000)



Len,n,m =map(int,input().split())
stone=[]    #石头i和到起点的距离
def check(d):
    num=0   # 可以搬走的数量
    pos=0 # 上一块石头位置
    for i in range(0,n):     # 0 - n-1 作为石头下标,总共n快石头
        if (stone[i]-pos<d): # 可以搬走
            num+=1
        else:  # 不能搬走
            pos=stone[i]

    if num<=m:  # 搬走的数量小于要求值,可以继续增大
        return True
    else:
        return False

for i in range(n):
    t=int(input())
    stone.append(t)
L,R=0,Len
while(L<R):
    mid=(L+R+1)//2
    if check(mid):
        L=mid
    else:
        R=mid-1
print(L)
        

3.青蛙过河(根据题意转为二分,即跳跃距离) 

'''
将所有的石头按照区间分类:1步可达的石头,2步可达的石头,,,,显然能通过一步走到,就绝对不通过两步去走
因为石头能被踩的次数是有限的,例如:1,2,3,4,假如最大步长为3,在1的时候,可以一步走到4号石头,但要是
从1走到2,再从2走到4,跟直接从岸上走到2,再从2走到4没有区别,因此,每一次移动,都必须移动到不同标号的区间去
有效的移动只有跨不同区间的移动
步长合法性判断:
对于任意石头i,区间[i,i+x)中的石头可被踩的总数>=2x
证明:
岸,1,2,3,|4,5,6,|7,8,9,岸
按照有效移动理论,在1时,下一步只能走到4去,因此,想要容纳1全部的被踩次数,4号的石头高度应>=1号,而显然,
想要总过河次数>=2x,那么区间[1,3]中石头总高度>=2x,因为出门的第一步必须要有2x次以上,即[1,3]总高度>=2x,
又因4号高度>=1号高度,故区间[2,4]高度之和>=2x,以此类推,可以证明,要想总过河次数>=2x,任一石头编号开始长度为
步长的区间石头总高度之和>=2x


再按照二分法搜索答案,适用二分法的特性:当步长为ans时可满足题目要求,ans+1一定可以满足题目要求
初始解区间[1,n],不断将解区间二分寻找到能够满足题目要求的最小解
'''
import sys
import collections
import itertools
import heapq
sys.setrecursionlimit(300000)


def check(mid):
    for i in range(mid,n):
        if sum[i]-sum[i-mid] <2*x:
            return False
    return True

n,x = map(int,input().split())
h = list(map(int,input().split()))
sum=[0]  # 前缀和下标从1开始
temp=0
for i in range(len(h)):   # 前缀和
    temp=temp+h[i]
    sum.append(temp)
L=0
R=n

while(L<R):
    mid=(L+R)//2
    if check(mid):
        R=mid
    else:
        L=mid+1
print(L)

 4.实数二分一元三次方程求解(送分)

import sys  #设置递归深度
import collections  #队列
import itertools  # 排列组合
import heapq  #小顶堆
import math
sys.setrecursionlimit(300000)
 
a,b,c,d = map(float,input().split())
def f(x):
   return x*x*x*a+x*x*b+x*c+d
 
ans=[]
for i in range(-100,100):
   if f(i)==0:
      ans.append(i)
   if f(i)*f(i+1)<0:  # 在(i,i+1)内有根
      l=i;r=i+1
      while(r-l>=0.001):  # 保留精度,2位小数,注意循环条件
         mid=(l+r)/2
         if f(l)*f(mid)<0:
            r=mid
         else:
            l=mid
      ans.append(l)
if f(100)==0:
   ans.append(100)
for i in ans:
   print("{:.2f}".format(i),end=' ')

猜你喜欢

转载自blog.csdn.net/weixin_52261094/article/details/130332719