动态规划

我们先来看著名的背包问题

01背包问题

有n件物品和一个容量为c的背包。(每种物品均只有一件)第i件物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可使价值总和最大。

这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。故称01背包问题。

首先我们得找到问题的状态方程:

假设 dp[i][j] 表示 前 i 件物品恰好放入一个容量为 j 的背包可以获得的最大价值。

dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]]+v[i])

代码实现

  • Python
def pack01(w,v,c):
    n = len(w)
    ### 注意我们这里i,j从 1 开始. dp[1][1]表示前1件物品容量为1的最大价值。
    dp = [[0 for _ in range(c+1)] for _ in range(n+1)]
    for i in range(1,n+1):
        for j in range(1,c+1):
            if j>=w[i-1]:
                dp[i][j] = max(dp[i-1][j],dp[i-1][j-w[i-1]]+v[i-1])
            else:
                dp[i][j] = dp[i-1][j]
    return dp[-1][-1]
w = [2,3,4,5]
v = [3,4,5,6]
c = 8
print(pack01(w,v,c))

完全背包问题

有n种物品和一个容量为c的背包,每种物品都有无限件可用。第i种物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

找到状态方程:

dp[i][j] = max(dp[i-1][j],dp[i-1][j-k*w[i]]+k*v[i]) 其中 k=1,2,3...

我们可以合并dp[i-1][j]dp[i-1][j-k*w[i]]+k*v[i]

dp[i][j] = max(dp[i][j],dp[i-1][j-k*w[i]]+k*v[i]) 其中 k=0,1,2...

代码实现

  • Python
def back_complete(w,v,c):
    n = len(w)
    dp = [[0 for _ in range(c+1)] for _ in range(n+1)]
    for i in range(1,n+1):
        for j in range(1,c+1):
            k = 0
            while j>=k*w[i-1]:
                dp[i][j] = max(dp[i][j],dp[i-1][j-k*w[i-1]]+k*v[i-1])
                k += 1
    return dp[-1][-1]
w = [2,3,4,5]
v = [3,4,5,6]
c = 8
print(back_complete(w,v,c))

多重背包问题

有n种物品和一个容量为c的背包。第i种物品最多有kk[i]件可用,每件重量是w[i],价值是v[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。

状态方程:

dp[i][j] = max(dp[i][j],dp[i-1][j-k*w[i]]+k*v[i]) 其中 k=0,1,2... 且k<=kk[i]

代码实现

  • Python
def back_multi(w,v,c,kk):
    n = len(w)
    dp = [[0 for _ in range(c+1)] for _ in range(n+1)]
    for i in range(1,n+1):
        for j in range(1,c+1):
            k = 0
            while j>=k*w[i-1] and k<=kk[i-1]:
                dp[i][j] = max(dp[i][j],dp[i-1][j-k*w[i-1]]+k*v[i-1])
                k += 1
    return dp[-1][-1]
w = [3,4,5]
v = [4,5,6]
kk = [1,1,1]
c = 10
print(back_multi(w,v,c,kk))