首先是 01 背包問題:
假設有很多商品每件商品都會占一定體積 v[x, y, z] (x,y,z是指某種商品占有的體積) 同時每件商品價值 w[x, y ,z] (對應於v里的商品所對應的價值)也不完全一樣,我們有兩種選擇我拿走或者不拿走,但是我的背包容量有限不能把所有商品全拿走,怎么辦才能使得我們取得商品總的價值最大。
首先這是一個動態規划問題,比如設我們取第n件商品的時候我們已經算出來取前n件商品的最大價值是f(n),那么我們在取第n+1件的時候要么取走要么不拿走,也就有兩種情況 A情況 這一種情況代表我們選擇要第n+1件商品
A = f(n) + w[n+1]
B情況 我們不要這一件商品。
B = f(n)
所以 f(n+1) = max(A, B)
我們就這樣從第一件一直判斷到第n件就能解決 01背包問題
代碼如下:
while True: try: N, V = (int(i) for i in input().split()) #這個是用來存商品體積的 ls_v = [0] #這個是用來存商品價值的 ls_w = [0] #我們建立一個 寬度是 背包容量加一高度是商品數量加一的列表 f = [[0 for i in range(V+1)] for i in range(N+1)] for i in range(N): v, w = (int(i) for i in input().split()) ls_v.append(v) ls_w.append(w) #這個是我們遍歷所有商品 for i in range(N+1): #這個是我們從體積為零一直增大到體積為V for j in range(V+1): #如果這個商品比我們的背包容量還大肯定要舍棄掉因此它的最優解肯定和它上一個解一樣 if ls_v[i] > j: f[i][j] = f[i-1][j] else: #這兩個分別代表我們取這一件商品和不取這一件商品 A = f[i-1][j] #如果取這一件商品那么背包肯定要被占據一定空間,然后價值也會增加這件商品的價值 B = f[i-1][j-ls_v[i]] + ls_w[i] f[i][j] = max(A, B) print(f[-1][-1]) except: break
這個我已經運行過肯定可以用。
然后我們可以對它進行優化,時間復雜度降不了了,但是空間復雜度可以減少。
優化代碼如下:
while True: try: N, V = (int(i) for i in input().split()) f = [0 for i in range(V+1)] def ZeroOnePack(cost, weight, n): for i in range(n, cost-1, -1): f[i] = max(f[i], f[i-cost]+weight) for i in range(N): v, w = (int(i) for i in input().split()) ZeroOnePack(v, w, V) print(f[-1]) except: break
接着是完全背包問題:
完全背包問題里 商品就不是每個只能選一個了,他假設每種商品都有無限多個,最后也是如何選取才能使得價值最大。
while True: try: N, V = (int(i) for i in input().split()) s = [0 for i in range(V+1)] ls_v = [0] ls_w = [0] for i in range(N): v, w = (int(i) for i in input().split()) ls_v.append(v) ls_w.append(w) for i in range(N+1): for j in range(V+1): if ls_v[i] <= j: s[j] = max(s[j], s[j-ls_v[i]]+ls_w[i]) print(s[-1]) except: break
或者寫成函數方程:
while True: try: N, V = (int(i) for i in input().split()) f = [0 for i in range(V+1)] def CompletePack(cost, weight, n): for i in range(cost, n+1): f[i] = max(f[i], f[i-cost]+weight) for i in range(N): v, w = (int(i) for i in input().split()) CompletePack(v, w, V) print(f[-1]) except: break
最后是多重背包問題:
多重背包問題就是商品都有一定數量,同樣也是如何選取能夠使得所選的組合價值最大。
這里有兩種思路一種就是轉換成01背包問題,比如一種商品 體積是 3 價值是 4 數量是 3就能轉化成 三個體積是3價值是4的商品。
代碼如下:
while True: try:
#N是有多少種商品,V是背包的體積 N,V = (int(i) for i in input().split()) ls_v = [0] ls_w = [0] S = [0 for i in range(V+1)] for i in range(N): v, w, s = (int(i) for i in input().split())
#我們每次得到一種商品商品總數就加上該種商品的數量 N += s-1 ls_v += [v]*s ls_w += [w]*s for i in range(1, N+1): for j in range(V, 0, -1): if ls_v[i] <= j: S[j] = max(S[j], S[j-ls_v[i]]+ls_w[i]) print(S[-1]) except: break
還有一種是使用一種是分解成01背包問題和無限背包問題while True try:
N,V = (int(i) for i in input().split()) f = [0 for i in range(V+1)] def ZeroOnePack(cost, weight, n): for i in range(n, cost-1, -1): f[i] = max(f[i], f[i-cost]+weight) def CompletePack(cost, weight, n): for i in range(cost, n+1): f[i] = max(f[i], f[i-cost]+weight) def MultiplePack(cost, weight, amount, n):
‘’‘
這里當數量很大都超過背包容量和無限次也就沒有區別了,反正背包又不能全裝走,比如背包容量為5商品體積是2
但是有三件很明顯背包裝不完
’‘’ if cost * amount > n: CompletePack(cost, weight, n) else: #這里只是為了把商品數量拆開而已,你完全可以把商品拆成一件一件的答案和我們拆成2的k次方是一樣的 k = 1 while k < amount: ZeroOnePack(k*cost, k*weight, n) amount -= k k *= 2 ZeroOnePack(amount*cost, amount*weight, n) for i in range(N): v, w, s = (int(i) for i in input().split()) #你也可以不判斷直接調用MultiplePack結果亦一樣只不過程序多走兩部而已。 if s == 1: ZeroOnePack(v, w, V) else: MultiplePack(v,w, s, V) print(f[-1]) except: break
再就是有的會把它所有組合一起,比如有的商品可以取無限次有的有限次有的只能一次,做法和我們上面寫的代碼完全一樣沒有區別,無非多了個判斷我們調用哪一個函數。
二維背包問題
二維背包問題就是背包不但有體積限制還有重量限制,其實和一維背包問題差不太多無非在建立元組時從原先的一維數組變成二維數組,所用的遞歸公式都差不多。f[v][m] = max(f[v][m], f[v-cost][m-mass] + worth)
while True: try: N, V, M = (int(i) for i in input().split()) f = [[0 for i in range(M+1)]for j in range(V+1)] def ZeroOnePack(cost, mass, worth, n, m): for i in range(n, cost-1, -1): for j in range(m, mass-1, -1): f[i][j] = max(f[i][j], f[i-cost][j-mass] + worth) for i in range(N): v, m, w = (int(i) for i in input().split()) ZeroOnePack(v, m, w, V, M) print(f[-1][-1]) except: break