1. 任务分配(二分:右半部分第一个满足的元素)
当ddl天数减少时,难以满足,当ddl天数增大时,之后都能满足,很明显满足二分性质。我们只需要二分找到第一个可以满足人员分配的ddl日期即可。
给定一个ddl日期,我们可以求出这个日期下,至少需要多少人,如果需要的人数超出目前的人数,说明无法满足。
def check(p):
global t
global k
cur = 1
# p天ddl的情况下,需要分配的cur人
b = [0] * (k+1)
for i in range(1,t+1):
# 这个人已经不能再接这个任务
if b[cur] + a[i] > p:
cur += 1
if cur > k:
return False
while b[cur] + a[i] > p:
cur += 1
b[cur] += a[i]
else:
b[cur] += a[i]
return True
def printf(p):
c = [[0,0] for i in range(k+1)]
b = [0] * (k+1)
cur = k
c[cur][1] = t
for i in range(t, 0, -1):
if b[cur] + a[i] > p:
c[cur][0] = i+1
cur -= 1
c[cur][1] = i
while b[cur] + a[i] > p:
cur += 1
b[cur] += a[i]
else:
b[cur] += a[i]
c[cur][0] = 1
for i in c[1:]:
print(i[0], i[1])
if __name__ == '__main__':
t, k = map(int, input().split())
a = [0]+list(map(int, input().split()))
l,r= max(a), sum(a)
while l < r:
mid = l + r >> 1
if check(mid):
r = mid
else:
l = mid + 1
printf(l)
2. 拦截罪犯(二分:右半部分第一个满足的元素)
通过二分枚举1~L的间距,得到每一个间距需要新增的警察数,找到第一个能满足cnt==k的间距。
此时的check条件是右半部分满足的条件,n越大越容易满足条件,我们要找到的是第一个满足的条件的位置。
def check(p):
cnt = 0
for i in range(1,n):
dis = a[i] - a[i-1]
a1, a2 = dis // p, dis %p
if a1:
if a2:
cnt += a1
else:
cnt += a1-1
if cnt > k:
return False
else:
return True
if __name__ == '__main__':
L,n,k = map(int,input().split())
a = list(map(int, input().split()))
a.sort()
l,r = 1, L
while l < r:
mid = l+r >> 1
if check(mid):
r = mid
else:
l = mid + 1
print(l)
3. 召唤神龙(二分:左半部分第一个满足的元素)
一道类似的题,但是此时check条件是左半部分满足的条件,因此我们使用二分法中寻找左半部分最后一个元素的模板。
def check(n):
# 假设此时组成n套牌
cnt = 0
for i in c:
if i >= n:
continue
cnt += n-i
# 需要补充的卡超出
if cnt > m or cnt > n:
return False
else:
return True
if __name__ == '__main__':
n,m = map(int, input().split())
c = list(map(int, input().split()))
max_num, min_num = max(c), min(c)
# 最多到那么多套
l, r = 0, max_num + m // n
while l < r:
mid = l+r+1 >> 1
if check(mid):
l = mid
else:
r = mid-1
print(l)