这里我先引用dd大牛的背包九讲中关于01背包算法的讲解。
所谓01背包问题可以描述为:有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。
这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。
情况一: 第i件不放进去,这时所得价值为:f[i-i][v]
情况二: 第i件放进去,这时所得价值为:f[i-1][v-c[i]]+w[i]
其状态转移方程为:
这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生出来的。所以有必要将它详细解释一下:“将前i件物品放入容量为v的背包中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵扯前i-1件物品的问题。如果不放第i件物品,那么问题就转化为“前i-1件物品放入容量为v的背包中”,价值为f[i-1][v];如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为v-c[i]的背包中”,此时能获得的最大价值就是f[i-1][v-c[i]]再加上通过放入第i件物品获得的价值w[i]。
典型的背包算法:
def deploy_01bag(count_all, capacity, goods):
'''
典型01背包算法
:param count_all: int, 输入的物品总数
:param capacity: int, 背包的容量
:param goods: list, 用于装包的物品参数list[[物品的id,物品占据的容量,
物品的价值],[],[]...]
:return: 返回装包方式,list[物品id1,物品id2,...] 最大价值 int
'''
bag = [[0 for col in range(capacity + 1)] for raw in range(count_all + 1)]
result = []
# 计算背包问题
for i in range(1, count_all + 1):
for j in range(1, capacity + 1):
bag[i][j] = bag[i - 1][j]
if j >= goods[i - 1][1]:
bag[i][j] = max(bag[i][j], bag[i - 1][j - goods[i - 1][1]] + goods[i - 1][2])
# 递推出装入背包的物体
big = bag[i][j]
j = capacity
for i in range(count_all, 0, -1):
if bag[i][j] > bag[i - 1][j]:
result.append(goods[i - 1][0])
j = j - goods[i - 1][1]
return result, big
if __name__ == "__main__":
capacity = 10
goods = [[1, 2, 6],\
[2, 2, 3],\
[3, 6, 5],\
[4, 5, 4],\
[5, 4, 6]]
result, big = deploy_01bag(len(goods), capacity, goods)
print("背包最能装下的最大价值为:{0}\n装包的物品有:{1}".format(big, result))
输出结果为:
背包最能装下的最大价值为:15
装包的物品有:[5, 2, 1]
再举个例子:
举一个具体的例子,在2018年的华为软件精英挑战赛中,给出的题目是预测未来某段时间的虚拟机的使用数量,并给出部署方案,使得每台物理服务器的资源占用率最大。其中物理服务器资源主要有两种。一种那是CPU,另一种是MEM(内存),且其值是固定的。要求优化CPU时,要使CPU的资源占用率最大,且还要满足放入的虚拟机的总的MEM不得超过物理服务器的MEM。
这样,这里的部署问题就是一个背包问题。但是跟01背包问题还有点区别。其中01背包问题是给定容量,使放置的价值总和最大。但是这个大小是没有限制的。但是,在这个问题中。如果我们要优化CPU,那么背包的总的容量就是物理服务器的MEM大小,我们的优化目的是使CPU(价值)最大,且这个最大值不能超过物理服务器的CPU值。
所以我们在进行背包迭代的时候,还需在循环中判断价值不能超过某一固定值。
def deploy_flavor_use_01bag(count_all, predict, server_CPU, server_MEM, target):
"""
01背包算法装包
:param count_all: int 输入的虚拟机总数
:param predict: list[list,list...] [型号,CPU,MEM]将每种虚拟机的型号参数数目写出来,
如[[10, 8, 8], [10, 8, 8], [10, 8, 8], [7, 4, 4], [7, 4, 4], \
[7, 4, 4], [7, 4, 4], [7, 4, 4], [7, 4, 4]]
:param server_CPU: int 物理服务器的CPU大小
:param server_MEM: int 物理服务器的Memory的大小
:param target: int 优化类型 1:CPU, 0:MEM
:return: list[list, list...] 每台物理服务器的装包方式 int 每台服务器最大装包结果
"""
if target:
server = server_MEM
limit_factor = server_CPU
capacity = 2 #every MEM
weight = 1 #every CPU
else:
server = server_CPU
limit_factor = server_MEM
capacity = 1 #every CPU
weight = 2 #every MEM
bag = [[0 for col in range(server + 1)] for raw in range(count_all + 1)]
result = []
# 计算背包问题
for i in range(1, count_all + 1):
for j in range(1, server + 1):
bag[i][j] = bag[i - 1][j]
if j >= predict[i - 1][capacity]:
bag[i][j] = max(bag[i][j], bag[i - 1][j - predict[i - 1][capacity]] + predict[i - 1][weight])
if bag[i][j] > limit_factor:
n = j - 1
for m in range(i, 0, -1):
if bag[m][n] > bag[m - 1][n]:
result.append(predict[m - 1][0])
n = n - predict[m - 1][capacity]
return result, bag[i][j - 1]
# 递推出装入背包的物体
big = bag[i][j]
j = server
for i in range(count_all, 0, -1):
if bag[i][j] > bag[i - 1][j]:
result.append(predict[i - 1][0])
j = j - predict[i - 1][capacity]
return result, big
最后再通过反推,将放置方案给输出出来。
更多的算法实现,参见的我的Github,如果觉得有用请别忘了点个Star哟~~~
参考blog: