一、问题提出
已知一个背包容量为 C ,有 n 个物品,其重量和价值分别为(w1,w2,...wn) 和 (v1,v2,...vn),要求将这个n个物品进行装包,使得不超出容量的前提下,背包内物品价值最大。使用贪心策略求解该问题, 体验贪心选择标准对贪心策略的意义。
二、思路分析
- 贪心算法是用来解决分数背包问题而非0-1背包问题(更多是动态规划之类)。分数背包问题是物品可以切割,最后一个物品放不下时,可以只放部分;而0-1背包问题是物品不能分割。
- 个人理解:贪心算法是贪图眼前利益,只要眼前最大的价值。背包问题的“价值”可以理解为三种——性价比贪心(V/W)、重量贪心(W)、价值贪心(V),通过对b站foretmer博主《6.2 贪心算法之背包问题》的认真学习,了解到可以用替代法证明性价比排序为最优的(时长30min),部分证明笔记如下(视频里有非常完整版,晚上听得很入迷睡不着)。http://【6.2 贪心算法之背包问题-哔哩哔哩】 https://b23.tv/WUPuxoA
3、整体思路:先输入背包总容量C,以及物品数量n,然后用随机函数取出每一个待装物品的重量和价值,并将他们放在一个数组中以变后续函数的调用。准备工作做好之后呢,就对(性价比、重量、价值)进行降序排列,然后进行装包处理。装包的时候分两种情况,如果所有物品能装进包就直接装,如果装不下,就只装部分。最后调用函数就结束啦!
三、简易版代码(按性价比权重排序)
这是以性价比为权重排序的版本,背包容量和物品数量是自定义,重量和价值为随机数
import numpy as np
C = eval(input("背包的容量为:"))
n = int(input("请输入有多少个物品:"))
# W定义待装物品重量在(1,100)之间取n个随机数
W = np.random.choice(range(1, 10), n)
# V定义待装物品价值在(1,100)之间取n个随机数
V = np.random.choice(range(1, 10), n)
# 定义一个数组
arr = [(i, V[i] / W[i], W[i], V[i]) for i in range(n)]
# 将数组按权重降序排序
arr.sort(key=lambda x: x[1], reverse=True)
print(arr)
# 初始化背包内总价值为0,背包内总重量为0
total_value = 0
total_weight = 0
bar_list = []
for j in range(n):
# 如果能放下宝物,就都放进去
if total_weight + arr[j][2] <= C:
total_weight += arr[j][2]
total_value += arr[j][3]
bar_list.append(j)
# 如果能放不下全部宝物,那就放部分
else:
remain = C - total_weight
total_weight = C
total_value += remain * arr[j][1]
break
list1 = max(bar_list) + 1
print("运走的总容量:", total_weight)
print("能运走的最大价值:", total_value)
print("宝物的数量:(包括只放了部分的最后一个)", list1)
四、详细版代码(将三种维度贪心进行对比)
非常详细的注释啦,如果没有理解视频里面关于性价比贪心最优的证明,不清楚性价比贪心、价值贪心、重量贪心哪个能运走的总容量最大,可以用代码科学证明下,定义三个函数ratio(性价比)、weight(重量)、value(质量),然后total_value最大值。
import numpy as np
# 定义背包容量
C = eval(input("背包的容量为:"))
# 定义背包物品数量
n = int(input("请输入有多少个物品:"))
# W定义待装物品重量在(1,10)之间取n个随机数,范围为可以自定义
W = np.random.choice(range(1, 10), n)
# V定义待装物品价值在(1,10)之间取n个随机数,范围可以自定义
V = np.random.choice(range(1, 10), n)
# 定义一个数组,其中V[i]/W[i]是每一单位物品性价比,第二项round函数是取小数点后3位
arr = [(i, round(V[i] / W[i], 3), W[i], V[i]) for i in range(n)]
# 小白为检验输入是否正确,输出检验下,也可也不写哦
print(arr)
# 将数组按性价比排序
def ratio():
# 将数组降序排序
arr.sort(key=lambda x: x[1], reverse=True)
# 初始化背包内总价值为0,背包内总重量为0
total_value1 = 0
total_weight1 = 0
for j in range(n):
# 如果能放下宝物,就都放进去
if total_weight1 + arr[j][2] <= C:
total_weight1 += arr[j][2]
total_value1 += arr[j][3]
# 如果能放不下全部宝物,那就放部分
else:
remain = C - total_weight1
total_value1 += remain * arr[j][1]
break
return total_value1
# 按重量排序,对arr中第三项降序排序,其他与上同
# 且改变变量名,将total_value1改为total_value2,total_weight1改为total_weight2
def weight():
arr.sort(key=lambda x: x[2], reverse=True)
total_value2 = 0
total_weight2 = 0
for j in range(n):
if total_weight2 + arr[j][2] <= C:
total_weight2 += arr[j][2]
total_value2 += arr[j][3]
else:
remain = C - total_weight2
total_value2 += remain * arr[j][1]
break
return total_value2
# 按价值排序,对arr中第四项降序排序,其他与上同
# 且改变变量名,将total_value1改为total_value3,total_weight1改为total_weight3
def value():
arr.sort(key=lambda x: x[3], reverse=True)
total_value3 = 0
total_weight3 = 0
for j in range(n):
if total_weight3 + arr[j][2] <= C:
total_weight3 += arr[j][2]
total_value3 += arr[j][3]
else:
remain = C - total_weight3
total_value3 += remain * arr[j][1]
break
return total_value3
# 用max函数选出三种定义中最大的价值,但其实替代法可以证明法一性价比价值最大
bar_max = max(ratio(), weight(), value())
# 输出函数,为保证美观,用round保留三位小数
print("能运走的最大价值(性价比贪心)", round(ratio(), 3))
print("能运走的最大价值(重量贪心)", round(weight(), 3))
print("能运走的最大价值(价值贪心)", round(value(), 3))
print("综上所述,能运走的最大价值为:", round(bar_max, 3))
五、详细版运行结果
六、好吧,ending
其实是自己做作业走了很多弯路,很想帮到大家。水平不够,有不足之处敬请指正,祝万事顺意!