動態規劃——揹包問題python實現(01揹包、徹底揹包、多重揹包)

參考:python

揹包九講——嗶哩嗶哩數組

揹包九講app



01揹包問題

01揹包問題ide

描述:函數

有N件物品和一個容量爲V的揹包。優化

第i件物品的體積是vi,價值是wi。code

求解將哪些物品裝入揹包,可以使這些物品的整體積不超過揹包流量,且總價值最大。htm


二維動態規劃

f[i][j] 表示只看前i個物品,整體積是j的狀況下,總價值最大是多少。 result = max(f[n][0~V]) f[i][j]:blog

  • 不選第i個物品:f[i][j] = f[i-1][j];

  • 選第i個物品:f[i][j] = f[i-1][j-v[i]] + w[i](v[i]是第i個物品的體積) 二者之間取最大。 初始化:f[0][0] = 0 (啥都不選的狀況,無論容量是多少,都是0?)

代碼以下:

n, v = map(int, input().split())
goods = []
for i in range(n):
    goods.append([int(i) for i in input().split()])

# 初始化,先所有賦值爲0,這樣至少體積爲0或者不選任何物品的時候是知足要求  
dp = [[0 for i in range(v+1)] for j in range(n+1)]

for i in range(1, n+1):
    for j in range(1,v+1):
        dp[i][j] = dp[i-1][j]  # 第i個物品不選
        if j>=goods[i-1][0]:# 判斷揹包容量是否是大於第i件物品的體積
            # 在選和不選的狀況中選出最大值
            dp[i][j] = max(dp[i][j], dp[i-1][j-goods[i-1][0]]+goods[i-1][1])


print(dp[-1][-1])


一維動態優化

從上面二維的狀況來看,f[i] 只與f[i-1]相關,所以只用使用一個一維數組[0~v]來存儲前一個狀態。那麼如何來實現呢?

第一個問題:狀態轉移

假設dp數組存儲了上一個狀態,那麼應該有:

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

max函數裏面的dp[i]表明的是上一個狀態的值。

第二個問題:初始化

這裏開始初始化一個長度爲V+1一維數組,選取0個物品時,體積爲0~V時的最大價值(值所有爲0)。

第三個問題:遞推關係

試想一下,我要保證求取第i個狀態時,用到一維數組中的值是第i-1個狀態的。若是,我從前日後推,那麼當我遍歷到後面時,我用到的狀態就是第i個狀態而不是第i-1個狀態。好比:

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

這裏的dp[i-v[i]]是已經從新賦值的,而不是上一個狀態的值,因此這樣是錯誤的。所以,咱們要從後往前推

n, v = map(int, input().split())
goods = []
for i in range(n):
    goods.append([int(i) for i in input().split()])

dp = [0 for i in range(v+1)]

for i in range(n):
    for j in range(v,-1,-1): # 從後往前
        if j >= goods[i][0]:
            dp[j] = max(dp[j], dp[j-goods[i][0]] + goods[i][1])

print(dp[-1])


肯定體積的狀況

若是我要求的不是儘量最大的價值,而是恰好等於揹包容量的最大價值,那麼該如何去作呢?


徹底揹包問題

徹底揹包問題

描述:

有N件物品和一個容量爲V的揹包,每件物品都有無限個!

第i件物品的體積是vi,價值是wi。

求解將哪些物品裝入揹包,可以使這些物品的整體積不超過揹包流量,且總價值最大。


一維動態規劃

徹底揹包問題跟01揹包問題最大的區別就是每個物品能夠選無數次,所以當咱們考慮到第i個物品時,咱們應該考慮的狀況是:不選這個物品、選一次這個物品、選兩次這個物品......,直到不能再選(選的次數k,k*v[i] > j,j爲當前揹包容量),而後再從這些狀況中選最大的。代碼以下:

n, v = map(int, input().split())
goods = []
for i in range(n):
    goods.append([int(i) for i in input().split()])
dp = [0 for i in range(v+1)]
for i in range(n):
    for j in range(v,-1,-1): # 從後往前
        k = j//goods[i][0]  # 能選多少次
        # 從這些次裏面取最大
        dp[j] = max([dp[j- x* goods[i][0]] + x * goods[i][1] for x in range(k+1)])

print(dp[-1])


一維動態規劃(優化)

剛剛那個問題,咱們是延續01揹包的問題,從後往前遞推。可是對於這個問題,其實能夠經過從前日後遞推。如何理解呢?

假設在考慮第i個物品時的兩個狀態:

A:dp[k*v[i] + x]

B:dp[(k-1)*v[i] + x]

根據前面的概括,從前一個狀態遞推過來:

  • 要求A的值,應該要從k+1個狀態中選出最大的:

    dp[x] + k*w[i]
    dp[v[i] + x] + (k-1)*w[i]
    dp[2*v[i] + x] + (k-2)*w[i]
    ...
    ...
    dp[(k-1)*v[i] + x] + w[i]
    dp[k*v[i] + x
  • 要求B的值,應該要從k個狀態中選出最大的:

    dp[x] + (k-1)*w[i]
    dp[v[i] + x] + (k-2)*w[i]
    dp[2*v[i] + x] + (k-3)*w[i]
    ...
    ...
    dp[(k-2)*v[i] + x] + w[i]
    dp[(k-1)*v[i] + x

咱們能夠看到,一一對應過來的話,這兩個狀態實際上只差一個w[i]的值。所以:

一方面咱們能夠根據前一個狀態(i-1)推出此時的狀態,另外一方面因爲當前狀態前面的值也是當前問題的子問題,所以咱們也能夠從前面的值推到後面的值。

從前日後遞推的代碼以下:

n, v = map(int, input().split())
goods = []
for i in range(n):
    goods.append([int(i) for i in input().split()])
dp = [0 for i in range(v+1)]
for i in range(n):
    for j in range(v+1):
        if j >= goods[i][0]:
            dp[j] = max(dp[j], dp[j-goods[i][0]] + goods[i][1])

print(dp[-1])

多重揹包問題

多重揹包問題

描述:

有N件物品和一個容量爲V的揹包。

第i件物品的體積是vi,價值是wi,數量是si。

求解將哪些物品裝入揹包,可以使這些物品的整體積不超過揹包流量,且總價值最大。


一維動態規劃

其實跟上面的徹底揹包問題相似,只不過咱們從後往前遞推的時候,物體i選取的次數應該要從新考慮下:

k = min(s[i], j//v[i]), j爲當前的揹包容量

代碼以下:

n,v = map(int, input().split())
goods = []
for i in range(n):
    goods.append([int(i) for i in input().split()])

dp = [0 for i in range(v+1)]

for i in range(n):
    for j in range(v, -1, -1):
        # 考慮兩種狀況的最小值
        k = min(j//goods[i][0], goods[i][2])
        dp[j] = max([dp[j-x*goods[i][0]] + x*goods[i][1] for x in range(k+1)])

print(dp[-1])


一維動態規劃(轉換01揹包)

想法很簡單,直接把揹包中的物品展開,展成不少數量爲1的物品,這樣就轉換爲01揹包問題。代碼以下:

n,v = map(int, input().split())
goods = []
for i in range(n):
    goods.append([int(i) for i in input().split()])

new_goods = []

# 展開
for i in range(n):
    for j in range(goods[i][2]):
        new_goods.append(goods[i][0:2])

goods = new_goods
n = len(goods)

# 01揹包問題
dp = [0 for i in range(v+1)]

for i in range(n):
    for j in range(v,-1,-1):
        if j>= goods[i][0]:
            dp[j] = max(dp[j], dp[j - goods[i][0]] + goods[i][1])

print(dp[-1])
相關文章
相關標籤/搜索