【蓝桥杯 算法提高】【打包】Python 【二分答案+二分模板】

题目

【蓝桥杯 算法提高】【打包】Python 【二分答案+二分模板】_第1张图片

分析

​ 共有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跳石头

【蓝桥杯 算法提高】【打包】Python 【二分答案+二分模板】_第2张图片
分析:
要让最短的跳跃距离最大,我们二分的找最短的跳跃距离!让其尽可能大

我们假设最短跳跃距离为mid,用红蓝区域模板。
接着我们写一个check函数,判断一下这个mid是否合法,如果合法,就尝试找一找有没有一个值比mid更大(l=mid)(拓展蓝色区域),如果不合法,就把mid减小(r=mid)。
check函数:我们遍历一遍每一块石头,累计出有多少块石头之间的间隔


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)

你可能感兴趣的:(蓝桥杯真题题解,蓝桥杯,python,算法)