问题描述https://www.lintcode.com/problem/backpack-ii/description
125. 背包问题 II
有 n
个物品和一个大小为 m
的背包. 给定数组 A
表示每个物品的大小和数组 V
表示每个物品的价值.
问最多能装入背包的总价值是多大?
关键点:
1.定义dp数组,dp[i][j]表示前i个物品,在背包大小为j的情况下的最大价值,dp[m][n]就是要求的结果。
2.求解过程。
没有商品,任意背包大小,最大价值都为0. 即dp[0][0] = 0, dp[0][1] = 0, ... , dp[0][n] = 0。
前一个商品,背包大小为0到m, 最大价值分别为dp[1][0], dp[1][1], ... , dp[1][n]
前两个商品,背包大小为0到m, 最大价值分别为dp[2][0], dp[2][1], ... , dp[2][n]
...
前i个商品,背包大小为0到m, 最大价值分别为dp[i][0], dp[i][1], ... , dp[i][n]
3.递推公式。dp[i][j] 与dp[i - 1][x]的关系, x属于[0, j]。 即“只有前i个物品且背包大小为j的总价值”与“只有前i-1个物品,背包大小为0到j”的关系. (A[i - 1]表示第i个商品的大小,V[i - 1]表示第i个商品的)
共有两种情况, 装入第i个商品或者不装入第i个商品。
装入第i个商品,则dp[i][j] = dp[i - 1][j - A[i - 1]] + V[i - 1]
不装入第i个商品,则dp[i][j] = dp[i - 1][j]
即递推公式为dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - A[i - 1]] + V[i - 1])
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
dp = [[0] * (m + 1) for i in range(len(A) + 1)]
for i in range(1, len(A) + 1):
for j in range(1, m + 1):
dp[i][j] = dp[i - 1][j]
if j >= A[i - 1]:
dp[i][j] = max(dp[i - 1][j], dp[i -1][j - A[i - 1]] + V[i - 1])
return dp[-1][-1]
def test():
s = Solution()
print s.backPackII(10, [3, 4, 8, 5], [1, 5, 2, 4]) == 9
print s.backPackII(10, [2, 3, 8], [2, 5, 8]) == 10
if __name__ == '__main__':
test()
dp空间优化分析1
1.dp[i][j]和dp[i - 1][x]有关系和其它所有行都没有关系, 因此可以用两行数组代替一个二维数组。dp_last表示上一行,dp_cur表示当前行。除dp变量其它逻辑没变。
(下面的dp_last = copy.deepcopy(dp_cur)通过值拷贝,否则dp_cur, dp_last两个变量指向同一块数据,更新dp_cur也同样更新了dp_last)
import copy
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
dp_last = [0] * (m + 1)
dp_cur = [0] * (m + 1)
for i in range(1, len(A) + 1):
for j in range(1, m + 1):
dp_cur[j] = dp_last[j]
if j >= A[i - 1]:
dp_cur[j] = max(dp_last[j], dp_last[j - A[i - 1]] + V[i - 1])
dp_last = copy.deepcopy(dp_cur)
return dp_cur[-1]
def test():
s = Solution()
print s.backPackII(10, [3, 4, 8, 5], [1, 5, 2, 4]) == 9
print s.backPackII(10, [2, 3, 8], [2, 5, 8]) == 10
if __name__ == '__main__':
test()
dp空间优化分析2
1.dp[i][j]和dp[i - 1][x]有关系和其它所有行都没有关系,与当前行也没有关系。因此可以顺序求dp[i][0], dp[i][1], ..., dp[i][m], 也可以逆序求dp[i][m], dp[i][m - 1], ..., dp[i][0]。
2.dp[i][j]只和前一行第x列元素有关(x属于[0, j]). 如果逆序求dp[i][j],这样dp_cur和dp_last可以合并, 因为更新dp_cur[j]的时候只需利用到dp_last第前j列的元素。而因为是逆序更新,更新dp_cur[j]的时候,dp_cur第前j列都没有变。
import copy
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
dp = [0] * (m + 1)
for i in range(1, len(A) + 1):
for j in range(m, 0, -1):
if j < A[i - 1]:
break
dp[j] = max(dp[j], dp[j - A[i - 1]] + V[i - 1])
return dp[-1]
def test():
s = Solution()
print s.backPackII(10, [3, 4, 8, 5], [1, 5, 2, 4]) == 9
print s.backPackII(10, [2, 3, 8], [2, 5, 8]) == 10
if __name__ == '__main__':
test()