【二分查找】

适用条件: 存在一个有序的数列;能够把题目建模为在有序数列上查找一个合适的数值。

整数二分:

在单调递增序列中找x或者x的后继

while lift<right:
  mid=(lift+right)//2
  if check(mid):
    lift=mid+1
  else:
    right=mid

在单调递增序列中找x或者x的前驱

while lift<right:
  mid=(lift+right+1)//2
  if check(mid):
    lift=mid
  else:
    right=mid-1

实数二分

实数二分相比较整数二分就没这么多限制

eps=0.00001 #精度
while right-left>eps:
  mid=(lift+right)/2
  if check(mid):
    lift=mid
  else:
    right=mid

for i in range(100):
  mid=(lift+right)/2
  if check(mid):
    lift=mid
  else:
    right=mid

分巧克力 二分+check(思维)

n,k=map(int,input().split())
a=[]
minx=float('inf')
for i in range(n):
  l=list(map(int,input().split()))
  minx=min(min(l),minx)
  a.append(l)

def check(x):
  nums=0
  for i in range(n):
    nums+=(a[i][0]//x)*(a[i][1]//x)
  if nums>=k:
    return True
  else:
    return False

l=1
r=100010

while l<r:
  mid=(l+r)//2
  print(mid)
  if check(mid):
    l=mid+1
  else:
    r=mid
print(l)

跳石头 二分+check(贪心)

套路题(最小值最大化,最大值最小化)

l,n,m=map(int,input().split())
a=[]
for i in range(n):
  a.append(int(input()))

lift=0
right=l

def check(x):
  num=0#当前搬运石头的次数
  d=0#前一个石块的位置
  for i in range(n):
    if a[i]-d<x:#说明需要搬走石块a[i],d在下一次中仍为a[i-1],故d不改变
      num+=1
    else:#说明不需要搬走石块,跟新d=a[i]
      d=a[i]
  if num<=m:#搬运次数没有超过m,说明可以满足条件
    return True
  else:
    return False

while lift<right:
  mid=(lift+right+1)//2
  #print(mid)
  if check(mid):
    lift=mid
  else:
    right=mid-1
print(lift)

一元三次方程求解

a,b,c,d=map(int,input().split())

l=-100
r=100

def cheak(x):
    return a*(x**3)+b*(x**2)+c*x+d

for i in range(-100,100):
    l=i
    r=i+1
    if cheak(l)==0:
        print('{:.2f}'.format(l),end=' ')
    if cheak(l)*cheak(r)<0:
        while r-l>=0.001:
            mid=(r+l)/2
            if cheak(mid)*cheak(r)<=0:
                l=mid
            else:
                r=mid
        print('{:.2f}'.format(r),end=' ')

青蛙过河

n,x=map(int,input().split())
a=list(map(int,input().split()))
sum=[0,a[0]]
for i in range(1,len(a)):#前缀和,第n个石头可以从前n-x中的任意一个跳过来
    sum.append(sum[i]+a[i])
# print(sum)
def cheak(mid):
    for i in range(mid,n):
        if sum[i]-sum[i-mid]<2*x:
            return False
    return True
l=0
r=100000
while l<r:
    mid=(l+r)//2
    if cheak(mid):
        r=mid
    else:
        l=mid+1
print(l)

技能升级

#暴力法
# n,m=map(int,input().split())
# a=[]
# for i in range(n):
#   l=list(map(int,input().split()))
#   a.append(l)
# cnt=0
# for i in range(m):
#   a.sort(key=lambda x:x[0])
#   cnt+=a[-1][0]
#   if a[-1][0]-a[-1][1]<0:
#       a[-1][0]=0
#   else:
#       a[-1][0]-=a[-1][1]
# print(cnt)


# from heapq import *
# n,m=map(int,input().split())
# q=[]
# for i in range(n):
#   a,b=map(int,input().split())
#   heappush(q,(-a,b))
# ans=0
# for i in range(m):
#   p=heappop(q)
#   a,b=-p[0],p[1]
#   ans+=a
#   a=max(a-b,0)
#   if a!=0:
#     heappush(q,(-a,b))
# print(ans)


n,m=map(int,input().split())
a=[]
b=[]
for i in range(n):
  a1,b1=map(int,input().split())
  a.append(a1)
  b.append(b1)

def cheak(mid):
  num=0
  for i in range(n):
    if a[i]>=mid:
      num+=(a[i]-mid)//b[i]+1 #加一是因为要将a[i]减到小于mid,如:17-15,要减两次
  if num>=m:#说明定太大了,升级次数无法完
    return True
  else:
    return False

l=1
r=1000000
while l<r:
  mid=(l+r+1)//2
  if cheak(mid):
    l=mid
  else:
    r=mid-1
# print(l)
ans=0
cnt=m
for i in range(n):
  if a[i]<l:
    continue
  t=(a[i]-l)//b[i]+1
  if a[i]-b[i]*(t-1)==l:
    t-=1
  ans+=(a[i]*2 - (t-1)*b[i])*t/2
  cnt-=t
print(int(ans)+cnt*l)

A Careful Approach

错解

f=1
while True:
    n=int(input())
    if n==0:
        break
    else:
        a=[]
        b=[]
        for i in range(n):
           a1,b1=map(int,input().split())
           a.append(a1)
           b.append(b1)
        if n==2:
            print('Case %d: %d:00' % (f, (max(b) - min(a))))
        else:
            #lens=(max(b) - min(a)) / (n - 2)
            def cheak(mid):
                d=a[0]
                for i in range(1,n):
                    d += mid
                    if d<a[i]:
                        d=a[i]
                    if d>b[i]:
                        return False
                return True
            l=0
            r=10000
            while r-l>0.000001:
                mid=(r+l)/2
                if cheak(mid):
                    l=mid
                else:
                    r=mid

            m1=r*60
            print('Case %d: %d:%02d'%(f,int(m1/60),int(m1%60+0.5)))
        f+=1

最少刷题数

n=int(input())
a=list(map(int,input().split()))
def cheak(mid):
    maxcnt=0
    mincnt=0
    for i in range(n):
        if a[i]<mid:
            mincnt+=1
        elif a[i]>mid:
            maxcnt+=1
        else:
            continue
    if mincnt>=maxcnt:#说明比自身小的多,该小点
        return True
    else:#说明比自身大的多,该大点
        return False

l = 0
r = 100000
while l < r:
    mid = (l + r) // 2
    #print(mid)
    #print(l,r)
    if cheak(mid):
        r = mid
    else:
        l = mid+1

maxn=0
minn=0
for i in range(n):
    if a[i]<l:
        minn+=1
    elif a[i]>l:
        maxn+=1
for i in a:
    if i>=l:
        print(0,end=' ')
    else:
        if minn == maxn:#因为小的要变成大的,会将小的部分减一,故应该再+1
            print(l-i+1,end=' ')
        else:
            print(l-i,end=' ')

123

#暴力+数论
# n=int(input())
# def Snn(x):
#     return x*(x+1)*(x+2)//6
# def Sn(x):
#     return x*(x+1)//2
# def f(x):
#     for i in range(1,int(1e6+5e5):
#         y=Sn(i)
#         if y>x:
#             return i,x-Sn(i-1)
#         if y==x:
#             return i,i
# for i in range(n):
#     a,b=map(int,input().split())
#     n1,n2=f(a)
#     m1,m2=f(b)
#     #print(n1,n2,m1,m2)
#     #print(Snn(n1),Sn(n2),Snn(m1),Sn(m2))
#     ans=Snn(m1-1)+Sn(m2)-Snn(n1-1)-Sn(n2-1)
#     print(ans)

n=int(input())
def Snn(x):
    return x*(x+1)*(x+2)//6
def Sn(x):
    return x*(x+1)//2
def f(x):
    l=0
    r=int(2e6+1)
    while l < r:
        mid = (l + r) // 2
        if Sn(mid) >= x:
            r = mid
        else:
            l = mid + 1
    return l,x-Sn(l-1)
for i in range(n):
    a,b=map(int,input().split())
    n1,n2=f(a)
    m1,m2=f(b)
    ans=Snn(m1-1)+Sn(m2)-Snn(n1-1)-Sn(n2-1)
    print(ans)

区间移位

求阶乘

高精度开根

最大子矩阵

扫地机器人

题目思路

二分+贪心

题目要求最少花费时间。

由于每个机器人的工作时间可能不同,那么这些机器人各自的花费时间中的最大值(设为 t )的就是本题要求的答案.

我们需要做的是使得 t 最小。将最大花费时间(t)最小化,显然我们需要使用二分求解。

我们假设某个机器人需要清扫 a,b,c,d 四个格子

因为这个机器人清扫完还需要回到最初始的位置,所以无论这个机器人初始位置在什么地方,其清扫路径的本质都是重复两次 a 到 b,b 到 c,c 到 d 的过程,花费时间为 6.

由此,我们假设某个机器人清扫的格子范围为 l, 那么这个机器人花费的时间为 2(l−1)×2。所以我们只需要对机器人清扫的范围(l)进行二分即可,最后的答案为 t=(l−1)×2。

显然当每个机器人清扫的范围大小相同时,花费时间最小。我们可以对清扫范围进行二分,然后验证其答案的正确性即可,判断条件是清扫范围可以使得每个格子都能够扫到.

我们可以明显的知道,最左边的格子由左边第一台机器人清扫,花费时间是最少的

我们可以采用贪心的思想,我们让每台机器人都要优先清扫其左边还未扫的到格子

然后再往右扫,在二分得到的范围下往右扫得越多越好

这样可以减少右边下一个机器人需要往左扫的范围,增加其往右扫的范围

以此类推,可减少清扫时间。

二分加贪心,主要是 check 函数的使用,二分的点就难在这里和边界,大家一定要理解!吃透它。

defcheck(a, x, n, k):
     #check()的作用是判断每个机器人扫地扫地范围为x可不可以将走廊清扫完sum = 0#  sum为当前已经打扫的过的最大位置,也是已经到过清扫过的格子的数量。想一想:因为采用了贪心的策略,从左到右每个机器人先清扫左边再清扫右边。所以sum为已经打扫过的最大位置。for i inrange(k):
        # 从左到右依次判断每个机器人清扫x范围,能否清扫完if a[i] <= x+sum:#说明之前打扫完的加上此刻往左打扫的可以达到a[i]的位置# a[i]-x判断从当前位置向左扫x个格子能否保证从当前位置的左边都清扫完毕if a[i] <= sum:#说明已经此刻向左的格子已经被之前的机器人打扫完了# 如果第i个机器人的位置已经打扫过了,说明只需要往右扫x个范围,且必须往右扫x个范围,因为采用贪心策略,往右扫的越多越好sum = a[i] + x - 1#为什么要减1,是因为=1代表机器人清理本身的区域。=2,=2才是往后前进1格# 直接扫x个范围,扫过的格子数为a[i]+x-1else:

                sum += x
                # 如果第i个机器人的左边位置还没有打扫,sum直接往右加x个格子,即打扫sum右边的x个位置。else:
            # 不能打扫完走廊,直接返回0returnFalsereturnsum >= n
    #如果sum>=n 说明每个机器人打扫x个格子情况下,可以将走廊打扫完if __name__ == "__main__":
    n, k = map(int, input().split())
    a = []

    for i inrange(k):
        #输入k个机器人的位置
        a.append(int(input()))

    a.sort()
    #按机器人位置从左至右排序

    l = 0
    r = n
    num = -1while l <= r:#注意是等于# 二分查找使时间花费最小的清扫范围

        m = (l + r) // 2#计算中点mif check(a, m, n, k):
            # 如果每个机器人打扫m个格子,可以将走廊打扫完,则记录当前打扫的范围,并尝试比m小的范围是否有满足条件的

            r = m - 1
            num = m
        else:
            # m个格子不能将走廊到扫完,从m+1开始继续二分查找

            l = m + 1print((num - 1) * 2)
    # 输出最终结果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值