适用条件: 存在一个有序的数列;能够把题目建模为在有序数列上查找一个合适的数值。
整数二分:
在单调递增序列中找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)
错解
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=' ')
#暴力+数论
# 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)
# 输出最终结果
