题目链接🔗:171. 送礼物 - AcWing题库
分析:
看到题目数据范围 N最大取46 根据背包问题的思想 每件物品有取和不取两种情况 那么总的时间复杂度就是O(2^46) 那一定会超时了 所以我们考虑能不能使用双向DFS 即 我们先预处理出一半的数据 也就是dfs一下前N/2个物品能取到哪些小于W的总重量 然后再dfs剩下一半的数据 第二次dfs出的剩下N/2个物品能凑出的总重量 只需要看一下第一组数中能与该数凑出最接近且不大于W的数是多少即可 这里我们可以先讲第一组的数排序一下然后二分查找答案即可。
具体解释请看注释
import bisect
W,N = map(int,input().split())
weight = [0]
for i in range(N) :
weight.append(int(input()))
total_weights = []
weight.sort(reverse=True) # 倒序排序加快搜索速度
ans = 0
K = N//2 + 2 # 前一半的数量
def dfs_1(now_depth,now_weight) :
global total_weights
if now_depth == K :
total_weights.append(now_weight)
return
dfs_1(now_depth+1,now_weight) # 不选这个物品
if now_weight + weight[now_depth] <= W :
dfs_1(now_depth+1,now_weight+weight[now_depth]) # 选这个物品
def dfs_2(now_depth,now_weight) :
global ans
if now_depth > N :
if W - now_weight in total_weights : # 如果前一组数中有刚好可以当前数凑出W的数
ans = W
return
else :
if now_weight == 0 :
return
tmp = bisect.bisect_left(total_weights,W-now_weight) # 二分查找 注意需要-1 因为bisect_left的含义是将当前数插入到total_weights中的位置,
# 所以应该找上一个数才是比他小的最大数
tmp -= 1
ans = max(ans,total_weights[tmp] + now_weight) # 更新答案
return
dfs_2(now_depth+1,now_weight) # 含义同上
if now_weight + weight[now_depth] <= W :
dfs_2(now_depth+1,now_weight+weight[now_depth])
dfs_1(0,0)
total_weights = list(set(total_weights)) # 去重
total_weights.sort() # 排序,有序的序列才能使用二分查找
dfs_2(K,0)
print(ans)