题目
http://118.190.20.162/view.page?gpid=T152
思路
- 暴力枚举所有情况,能过70%;
- 回溯+剪枝,能过70%。应该还可以有其他优化,我的只过70.
- 类比背包问题 动态规划。这个需要反向思考。
如一个例子:
books: 20 30 60 60 90 计算得到所有数的和 sum:260 limit: 100
目的是找到一个组合的和是 超过limit的最小值,那么反过来就是,找到一个组合的和是 不超过sum-limit的最大值。
这里可以转化为,找到一个组合的和是不超过 sum-limit=160的最大值,也就相当于背包问题中,限制了背包容量,求这个容量下能获得的最大价值。
值得注意的是,背包问题中每个物品都有容量和价值两个属性,目的是最大化价值。然而,上面转化后只有一个容量属性,没有价值属性,目的是最大化容量。那么,容量就视为价值。
就上面例子:
容量160,可以选60 90
这个组合,容量150是不超过160的最大容量,同时也是要找的‘最大价值’。
代码
n, limit = map(int, input().split())
nums = [0] * (n+1)
sum = 0
for i in range(1, n+1):
nums[i] = int(input())
sum += nums[i]
'''
目的:用nums中的数组合 超过limit的最小值 转换为 不超过 sum - limit 的最大值,看作背包问题
将 sum - limit 作为背包容量,求这个容量下最大价值
如:20 30 60 60 90 sum=260, limit = 100, V = sum - limit = 160
用容量为160的背包,找价值最大的组合(隐含了,将价值视作和物品容量等值)
'''
V = sum - limit
# initialize dp : (n + 1) * ( V + 1)
dp = [[0]*(V+1) for i in range(n+1)]
# dp: dp[i][j] for max value when choosing from top i objects
for i in range(1, n+1):
for j in range(1, V+1):
# check if nums[i] can be choosed
if j < nums[i]:
dp[i][j] = dp[i-1][j]
else:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-nums[i]] + nums[i])
print(sum - dp[n][V])
总结
- 将超过limit的最小值转化为不超过sum-limit的最大值,从而转换为背包问题,逆向思维,转化思想。
- 根据问题的目的,这里需要将容量和价值看作等价的。