写在前面,如果有更好的方法可以给博主分享一下么,木有vip,看不到lintcode的题解,谢谢啦
题目描述
https://www.lintcode.com/problem/backpack-iv/description
给出 n 个物品, 以及一个数组, nums[i]
代表第i个物品的大小, 保证大小均为正数并且没有重复, 正整数 target
表示背包的大小, 找到能填满背包的方案数。每一个物品可以使用无数次
样例1
输入: nums = [2, 3, 6, 7] 和 target = 7
输出: 2
解释:
方案有:
[7]
[2, 2, 3]
样例2
输入: nums = [2, 3, 4, 5] 和 target = 7
输出: 3
解释:
方法有:
[2, 5]
[3, 4]
[2, 2, 3]
题解
方法一: 递归
递归,暴力穷举所有可能性,没啥可说的很容易理解。解释一下这一段,由于递归的时候传递的是i而不是i+1,是因为题目中可以重复取(即下次递归的时候还可以继续取用i),然后某一轮递归结束,回到该i时,因为target-nums[i]小于零(取过后面的值),则会跳过该i,即不取用。
for i in range(index, len(nums)):
if target - nums[i] >= 0 :
res += self._helper(nums, i, target - nums[i])
class Solution:
"""
@param nums: an integer array and all positive numbers, no duplicates
@param target: An integer
@return: An integer
"""
def __init__(self):
self.memo = []
def backPackIV(self, nums, target):
# write your code here
if not nums or target <= 0:
return 0
return self._helper(nums, 0, target)
def _helper(self, nums, index, target):
if index >= len(nums) or target < 0:
return 0
if target == 0:
return 1
res = 0
for i in range(index, len(nums)):
if target - nums[i] >= 0 :
res += self._helper(nums, i, target - nums[i])
# 下面注释掉是因为,其实不取即取0次,已经包含进上面了,直接跳过去了
# res += self._helper(nums, i + 1, target)
return res
方法二:记忆化搜索
由递归到记忆化搜索很水到渠成
class Solution:
"""
@param nums: an integer array and all positive numbers, no duplicates
@param target: An integer
@return: An integer
"""
def __init__(self):
self.memo = []
def backPackIV(self, nums, target):
# write your code here
if not nums or target <= 0:
return 0
self.memo = [[-1] * (target + 1) for _ in range(len(nums))]
return self._helper(nums, 0, target)
def _helper(self, nums, index, target):
if index >= len(nums) or target < 0:
return 0
if target == 0:
return 1
if self.memo[index][target] != -1:
return self.memo[index][target]
res = 0
for i in range(index, len(nums)):
if target - nums[i] >= 0 :
res += self._helper(nums, i, target - nums[i])
self.memo[index][target] = res
return res
方法三:动态规划
解释一下:times - 是最多可以取用i的次数,time=0表示不取用,times是可重复的标志
for i in range(1, len(nums)):
for j in range(target + 1):
# 下面的注释掉,是因为当time=0时,就是不去用i
# dp[i][j] = dp[i-1][j]
# times - 是最多可以取用i的次数,time=0表示不取用,times是可重复的标志
times = j // nums[i]
for time in range(0, times + 1):
dp[i][j] += dp[i - 1][j - time * nums[i]]
class Solution:
"""
@param nums: an integer array and all positive numbers, no duplicates
@param target: An integer
@return: An integer
"""
def backPackIV(self, nums, target):
# write your code here
if not nums or target <= 0:
return 0
dp = [[0] * (target + 1) for _ in range(len(nums))]
for j in range(target + 1):
if j % nums[0] == 0:
dp[0][j] = 1
for i in range(1, len(nums)):
for j in range(target + 1):
# dp[i][j] = dp[i-1][j]
times = j // nums[i]
for time in range(0, times + 1):
dp[i][j] += dp[i - 1][j - time * nums[i]]
return dp[-1][-1]
空间上的优化
与0-1背包类似,只不过这边要加如所有的可能性(因为可重复,[0,times])
for i in range(1, len(nums)):
for j in range(target, -1, -1):
times = j // nums[i]
dp[j] = sum([dp[j - time * nums[i]] for time in range(0, times + 1)])
# for time in range(1, times + 1):
# dp[j] += dp[j - time * nums[i]]
class Solution:
"""
@param nums: an integer array and all positive numbers, no duplicates
@param target: An integer
@return: An integer
"""
def backPackIV(self, nums, target):
# write your code here
if not nums or target <= 0:
return 0
dp = [0] * (target + 1)
for j in range(target + 1):
if j % nums[0] == 0:
dp[j] = 1
for i in range(1, len(nums)):
for j in range(target, -1, -1):
times = j // nums[i]
dp[j] = sum([dp[j - time * nums[i]] for time in range(0, times + 1)])
# for time in range(1, times + 1):
# dp[j] += dp[j - time * nums[i]]
return dp[-1]