题目
分析
共有N个礼物,需要进行M次打包,希望让每个人得到的包裹连续且最大重量最小,假设所有礼物的总重量为sum ,所有礼物中最重的礼物为max
,那么答案只有能可能出现[max,sum] 这个区间内部,去二分出最小的那个能打m包的最大重量
举个例子理解一下:!!(一定多推推!)
答案一定在[max,sum]区间内,使用二分来找具体是哪一个值,定义枚举的值是Target,举个栗子:加入礼物重量分别是:1,2,3,4,5
,要打成3个包裹,最大值是5,和是15,也就是说答案只有可能在[5,15]这个范围内,那么二分枚举中间值Target=(5+15)/2
=10,那么10这个最大重量是否合适呢?因为礼物只能连续打包,因此打包为:【1,2,3,4】【5】只需要两个包,但是题目中给定最大打包数量是3个,要求最小最大重量,显然3个包的情况下最大重量可以更小,也就是说答案的区间缩小到[5,10],同理枚举Target=(5+10)/2
= 7,礼物打包为:【1,2,3】【4】】【5】,打包数量为3个,题给定的打包数量也是3个,那么7就是最终答案了吗?不一定因为5,6两种情况还没有枚举,此时答案区间缩小为[5,7],接着枚举6,打包为【1,2,3】【4】【5】,打包数量为3个,缩小区间为[5,6],最后枚举5,【1,2】【3】【4】【5】,打包数量为4个,超过了题目给定的打包数量,也就是说答案ans>5,因此缩小区间为[6,6],最终得到答案。通过judge函数判断返回不断"尝试"能否在给定包数量内打包成功并缩小区间
最大重量最小,我们二分答案找到肯定是最大重量!
一开始写的二分模板 70分
我们看二分最大重量mid是否合法,如果合法,是否还能再小 r=mid,否则就大一些 l=mid
check函数:我们遍历一遍每一个物品,如果当前物品重量>打包最大重量,我们无法装下去,不合法;否则把物品打包,如果打包后总重量>最大重量,我们必须另外重新打一个包装它,最终看看是否打了m个包,如果小于m个,就缩小最大重量区间再找一个小的
while l+1!=r:#蓝红区域
mid=(l+r)//2
if check(mid):
r=mid
else:
l=mid
print(r)
下面是y总的二分模板ac
n,m=map(int,input().split())
a=list(map(int,input().split()))
l=max(a)
r=sum(a)
def check(mid):
global m,a
curWeight=0
curPack=1
for val in a:#对应每个物品
if val >mid:#如果当前物品超出了最大打包重量,不能打成任何一个包
return False
curWeight +=val#可以装下装入这个包
if(curWeight>mid):#如果装下后超出了单个包的最大打包重量,需要重新打包
curWeight=val#放入另一个包
curPack+=1
return curPack<=m#是否用了m个包,如果不是,继续缩小范围MAX--MID 保证最大的重量最小
while l<r:#蓝红区域模板还不能用,用普通的套
mid=(l+r)//2
if check(mid):#还能再小
r=mid
else:
l=mid+1
print(l)
补充:红蓝边界是可行的!只是调试了好几次才ac,问题在l r 的初始值上!!一开始都指向最外面!!!
l=max(a)-1
r=sum(a)+1
AcWing跳石头
分析:
要让最短的跳跃距离最大,我们二分的找最短的跳跃距离!让其尽可能大
我们假设最短跳跃距离为mid,用红蓝区域模板。
接着我们写一个check函数,判断一下这个mid是否合法,如果合法,就尝试找一找有没有一个值比mid更大(l=mid)(拓展蓝色区域),如果不合法,就把mid减小(r=mid)。
check函数:我们遍历一遍每一块石头,累计出有多少块石头之间的间隔<mid(因为mid是假定的最小的距离,必须把它移走),如果超过m个,就不合法,如果小于等于m,就合法
a=[0]
def check(mid):#check函数判断这个最短跳跃距离mid是否合法
cnt=0#last表示的是上一块石头的位置,cnt用来计数
last=0
for i in range(1,n+1):#枚举每一块石头
if a[i]-last<mid:#如果这一块石头和上一块石头的距离比mid小,要移走这块石头a[i],计数+1。而且如果石头移走,last还是上一块石头的位置
cnt+=1
else:
last=a[i]#否则这块石头就不必移走,更新last=这块石头的位置。
if cnt>m: return False#cnt如果超过m个,就不合法。
else :return True
L,n,m=map(int,input().split())
for i in range(n):
a.append(int(input()))
a.append(L)
#print(a)
l=-1
r=L+1
while l+1!=r:
mid=(l+r) //2
if check(mid):
l=mid#拓展蓝色区域
else:
r=mid
print(l)