动态规划求解01背包
01背包问题描述:
01背包问题可以假设为现在有一堆物品,每一个物品都具有两个属性,物品的重量和价值。现在有一个承重有限的背包,给定背包的最大承受重量。现在要将物品装入背包,使得背包里所有物品的价值总和最大,我们应该放哪些物品进入背包。
动态规划:
动态规划算法通常所用于求解具有某种最有性质的问题。在某一个具体的问题中,可能会存在很多可行解,而我们所需要的就是去寻找这众多可行解中的最优解。动态规划的思想也和分治算法的思想类似,动态规划算法也是将原问题分解为若干个子问题,然后根据子问题的解来得到原问题的最优解。动态规划算法和分治法不同的是,分治法分解得到的若干个子问题是互相独立的,相互没有约束。而动态规划算法得到的子问题的解之间相互约束,从规模最小的子问题到原问题中的每一级,较高级的子问题的解是由较低级的子问题的最优解得到的,这样保证了每一级所得到的解都是当前子问题的最优解。最终得到的原问题的解也便是最优的。但动态规划算法只能得到最优解中的某一个,而不能得到所有的最优解。
具体步骤:
(1) 把原问题分解成各个子问题,如背包的重量为i(0<=i<=背包最大承重)时,面对前j(1<=j<=n)件物品时,背包所能获取的最大价值。
(2) 依次求出每个子问题的最优解,并且每一个问题的最优解都是由前一个问题的最优解得到,找到其状态转移方程。
(3) 最终通过各个子问题的最优解得到原问题的最优解。
关键代码思想:
(1) 初始化部分将输入的物品的重量和价值分别用一个列表来储存,采用split()函数以“ ”为界限划分,再将其中的每一个元素转化为int型,append到value[]和weight[]列表中。
(2) 设置一个二维数组来保存当面对前i件物品时,背包承重为j时,背包所能获得的最大价值。python中的二维数组用列表生成式来定义,max_value = [[0]*(bag_weight+1) for i in range(0,n+1)],创建一个长度为n+1的列表,并且里面的每个元素也均为列表,且长度均为bag_weight+1,并且将其全部赋初值为0。
(3) 求取状态转移方程,当面对第i个物品时,背包承重j所能获得的最大价值为当面对第i-1个物品时背包承重为j的最大价值或者为第i个物品腾出空间,获得价值为面对前i-1个物品时,背包承重为j-weight[i]时的最大价值加上第i件物品的价值。因此得到的状态转移方程为:
if(j>=weight[i]):
max_value[i][j] = max(max_value[i-1][j], max_value[i-1][j-weight[i]]+value[i])
else:
max_value[i][j] = max_value[i-1][j]
(4)接下来判断背包承重为bag_weight时,重新定义一个列表来保存所拿的物品(拿了为1,没拿为0),将max_value[][bag_weight]从后往前比较,若此时的max_value与前一个值相等的话,则证明此件物品没有拿,如果不相同,则证明拿了此件物品,并且将bag_weight减去该物品的质量,继续上述过程。
(5) 计算程序运行的时间,导入time模块,用该模块中的clock函数标记程序输入完成后开始的时间和最后输出结果后的时间,将两个时间进行做差值来计算程序运行的时间。
实例解析:
物品数量:7
每件物品价值:5 4 7 6 5 8 4
每件物品质量:1 3 2 4 5 3 2
背包最大承重:13
根据以上思路便能得到一下表格,横轴坐标为面对第i(i从0到7)件物品,纵轴坐标为背包承重为j(j从0到13)时,背包所对应的最大价值。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
1 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 |
2 | 5 | 5 | 5 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 |
3 | 5 | 7 | 12 | 12 | 12 | 16 | 16 | 16 | 16 | 16 | 16 | 16 | 16 |
4 | 5 | 7 | 12 | 12 | 12 | 16 | 18 | 18 | 18 | 22 | 22 | 22 | 22 |
5 | 5 | 7 | 12 | 12 | 12 | 16 | 18 | 18 | 18 | 22 | 22 | 23 | 23 |
6 | 5 | 7 | 12 | 13 | 15 | 20 | 20 | 20 | 24 | 26 | 26 | 26 | 30 |
7 | 5 | 7 | 12 | 13 | 16 | 20 | 20 | 24 | 24 | 24 | 28 | 30 | 30 |
具体代码的实现:
import time
def main():
print("请输入一共有多少个物品:",end = " ")
n = int(input())
value = []
value.append(0)
weight = []
weight.append(0)
print("请输入每个物品的价值:",end = " ")
value_all = input().split(" ")
for value_one in value_all:
value.append(int(value_one))
print("请输入每个物品的重量:",end = " ")
weight_all = input().split(" ")
for weight_one in weight_all:
weight.append(int(weight_one))
print("请输入背包重量:",end = " ")
bag_weight = int(input())
time1 = float(time.clock())#保存程序开始时的时间
max_value = [([0]*(bag_weight+1)) for i in range(0,n+1)]
for i in range(1,n+1):
for j in range(1,bag_weight+1):
if(j>=weight[i]):
max_value[i][j] = max(max_value[i-1][j],max_value[i-1][j-weight[i]]+value[i])
else:
max_value[i][j] = max_value[i-1][j]
print("当背包重量为%d时,能拿物品的最大价值为:%d"%(bag_weight,max_value[n][bag_weight]))
item = [0 for i in range(n+2)]
m = n
while m>=1:
i = m
if(max_value[i][bag_weight] == max_value[i-1][bag_weight]):
item[i] = 0
else:
item[i] = 1
bag_weight-=weight[i]
m-=1
print("所能拿的物品为(1为拿,0为不拿):",end = " ")
for i in range(1,n+1):
print(item[i],end = " ")
time2 = float(time.clock())
print("\n程序运行时间:%f"%(time2-time1))
if __name__ == '__main__':
main()
测试记录:
注意事项:
物品价值和重量输入的时候,每一个元素之间有且只有一个空格隔开,并且行末不能存在空格。