首先是 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