详细解析参考“背包九讲”:https://github.com/tianyicui/pack
目录
1. 背包问题 I —— 0-1背包无价值
2. 背包问题II —— 0-1背包有价值
3. 背包问题III —— 完全背包问题
小结1
4. 背包问题IV / V —— 求方案数
5. 背包问题VII —— 多重背包问题
6. 多重背包可行性解
7. 换零钱问题
0-1背包问题,无价值:https://www.lintcode.com/problem/backpack/description
问题描述:Given n items with size Ai, an integer m denotes the size of a backpack. How full you can fill this backpack?
解析:
递推公式:
:前i个物品放入容量为j的背包的最大重量;
:放入第i个时的重量;
:不放第i个时的重量;
第一层循环 i:0~n
第二层循环 j:m ~ Ai,倒序是因为每个物品只能用一次。
可简化成一位数组。
class Solution:
"""
@param m: An integer m denotes the size of a backpack
@param A: Given n items with size A[i]
@return: The maximum size
"""
def backPack(self, m, A):
n = len(A)
if n <= 0 or m <= 0:
return 0
dp = [0 for _ in range(m+1)]
for i in range(n):
for j in range(m,A[i]-1,-1):
dp[j] = max(dp[j-A[i]] + A[i], dp[j])
return dp[-1]
0-1背包问题有价值:https://www.lintcode.com/problem/backpack-ii/
问题描述:
Given n items with size Ai and value Vi, and a backpack with size m. What's the maximum value can you put into the backpack?
Example:
Given 4 items with size
[2, 3, 5, 7]
and value[1, 5, 2, 4]
, and a backpack with size10
. The maximum value is9
.思路:同上
class Solution:
"""
@param m: An integer m denotes the size of a backpack
@param A: Given n items with size A[i]
@param V: Given n items with value V[i]
@return: The maximum value
"""
def backPackII(self, m, A, V):
# write your code here
n = len(A)
if m <= 0 or n <= 0:
return 0
dp = [0 for _ in range(m + 1)]
for i in range(n):
for j in range(m,A[i]-1,-1):
dp[j] = max(dp[j-A[i]]+V[i], dp[j])
return dp[m]
完全背包问题,有价值无限个数:https://www.lintcode.com/problem/backpack-iii/description
问题描述:
Given n kind of items with size Ai and value Vi(
each item has an infinite number available
) and a backpack with size m. What's the maximum value can you put into the backpack?Example:
Given 4 items with size
[2, 3, 5, 7]
and value[1, 5, 2, 4]
, and a backpack with size10
. The maximum value is15
.思路:
递推公式:
:同理;
:放入第i个物品时,不管上一次放没放入过该物品;
:不放入第i个物品时;
第一层循环i:0~n
第二层循环j:Ci~m,正向,因为不用顾忌每个物品只能放一次。
class Solution:
"""
@param A: an integer array
@param V: an integer array
@param m: An integer
@return: an array
"""
def backPackIII(self, A, V, m):
# write your code here
n = len(A)
if n <= 0 or m <= 0:
return 0
dp = [0 for _ in range(m+1)]
for i in range(n):
for j in range(A[i], m+1):
dp[j] = max(dp[j-A[i]] + V[i], dp[j])
return dp[-1]
0-1背包问题 vs 完全背包问题,实质性区别在于第二层循环一个是倒序,一个是正序:
0-1 背包问题,返回方案数:https://www.lintcode.com/problem/backpack-v/description
完全背包问题,返回方案数:https://www.lintcode.com/problem/backpack-iv/description
以完全背包问题为例:
Given n items with size
nums[i]
which an integer array and all positive numbers, no duplicates. An integertarget
denotes the size of a backpack. Find the number of possible fill the backpack.
Each item may be chosen unlimited number of times
Example:
Given candidate items
[2,3,6,7]
and target7
,A solution set is: [7] [2, 2, 3]
return 2
解析:
将完全背包问题里面的max改为sum,且初始化时将dp[0] 设为1;
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
n = len(nums)
if n <= 0 or target < 0:
return 0
dp = [0 for _ in range(target+1)]
dp[0] = 1
for i in range(n):
for j in range(nums[i],target+1):
dp[j] = dp[j - nums[i]] + dp[j]
return dp[-1]
多重背包问题,每个物品有限个个数,求最大价值:https://www.lintcode.com/problem/backpack-vii/description
问题描述:
Assume that you have
n
yuan. There are many kinds of rice in the supermarket. Each kind of rice is bagged and must be purchased in the whole bag. Given theweight
,price
andquantity
of each type of rice, findthe maximum weight
of rice that you can purchase.Example:
Given: n = 8 prices = [2,4] weight = [100,100] amounts = [4,2] Return:400
基本思路:
假设有N类物品,每个物品有个,则可以转换成 个0-1背包问题。则时间复杂度为:,,V为背包容量,M是每个物品的个数的集合。
进阶思路:
import math
class Solution:
"""
@param n: the money of you
@param prices: the price of rice[i]
@param weight: the weight of rice[i]
@param amounts: the amount of rice[i]
@return: the maximum weight
"""
def backPackVII(self, n, prices, weight, amounts):
#将每一类物品转化为k个子物品,求得对应的价钱,重量,转化成0-1背包问题
#先生成新的物品集合
new_prices = []
new_weight = []
for i in range(len(amounts)):
k = int(math.log(amounts[i],2))+1
coefs = [2**i for i in range(k - 1)]
coefs.append(amounts[i] - 2**(k-1) + 1)
for item in coefs:
new_prices.append(item*prices[i])
new_weight.append(item*weight[i])
#0-1背包问题
dp = [0 for _ in range(n+1)]
for i in range(len(new_prices)):
for j in range(n, new_prices[i]-1, -1):
dp[j] = max(dp[j-new_prices[i]] + new_weight[i], dp[j])
return dp[-1]
多重背包问题求可行性解的个数:https://www.lintcode.com/problem/backpack-viii/description
问题描述:
Give some coins of different value and their quantity. Find how many values which are in range
1 ~ n
can these coins be combinedExample
Given: n = 10 value = [1,2,4] amount = [2,1,1] Return: 8 They can combine all the values in 1 ~ 8
思路:
class Solution:
"""
@param n: the value from 1 - n
@param value: the value of coins
@param amount: the number of coins
@return: how many different value
"""
#多重背包可行性问题 O(VN)
def backPackVIII(self, n, value, amount):
# write your code here
dp = [-1 for _ in range(n+1)]
dp[0] = 0
for i in range(len(value)):
for j in range(n+1):
if dp[j] >= 0:
dp[j] = amount[i]
else:
dp[j] = -1
for j in range(n-value[i]+1):
if dp[j] > 0:
dp[j+value[i]] = max(dp[j+value[i]], dp[j] - 1)
return n - dp.count(-1)
即完全背包问题求解最小的使用个数。:https://www.lintcode.com/problem/coin-change/description
问题描述:
You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return
-1
.Example:
Given coins =
[1, 2, 5]
, amount =11
return3
(11 = 5 + 5 + 1)Given coins =
[2]
, amount =3
return-1
.Notice:
You may assume that you have an infinite number of each kind of coin.
思路:完全背包问题,转换成最小组合数。
class Solution:
"""
@param coins: a list of integer
@param amount: a total amount of money amount
@return: the fewest number of coins that you need to make up
"""
def coinChange(self, coins, amount):
# write your code here
dp = [float('inf') for _ in range(amount+1)]
dp[0] = 0
for i in range(len(coins)):
for j in range(coins[i],amount+1):
dp[j] = min(dp[j-coins[i]] + 1, dp[j])
if dp[-1] < float('inf'):
return dp[-1]
else:
return -1