贪心算法解决(物品不能分割)背包问题

贪心算法原理:

           每走一步,选出在这一步的最好选择。试图通过局部的最优(目光短浅)来实现整体的最优,但是从整体上看,又不是最优解。

题目:

          一个背包,背包容量是M=140。有8个物品,物品不可以分割成任意大小。要求尽可能让装入背包中的物品总价值最大,但不能超过总容量。 物品  A        B       C       D      E        F       G       H

                           重量 25       30      6       50    40     10      25      3

                           价值 10       40     30      50    35      40     30     15

第一次尝试:

         设计思路:用字典把物品代号及其重量~价值~单位重量的价值相互对应起来(此时用到zip函数:把两个列表里的数联合到一个字典里),再对物品的单价(我把物品的单价作为字典里的 “ 值 ”,把物品代号作为 “ 键  ”)进行大小排序,(此时又用到zip的另一个作用,默认对其对象里的第一个进行升序排序),排序后,我想通过for循环对其进行从后往前的遍历,即:当遍历到倒数第一个时,访问其对应的物品代号(即通过“值”访问“键”),在通过“键”(物品代号)去找出其对应重量与价值,判断是否满足其中重量是否能满足放进背包。再遍历倒数第二个,进行同样的操作,直到物品总重量超过背包容量。最后输出结果。(感觉这不是贪心算法了)

         遇到的问题:程序复杂是次要问题,编写不出 “当遍历到倒数第一个时,访问其对应的物品代号(即通过“值”访问“键”),在通过“键”(物品代号)去找出其对应重量与价值,判断是否满足其中重量是否能满足放进背包。”才是主要问题。其中不知道怎样实现通过“值”访问“键”,  虽然写出来了相关代码,但是运行结果不是正确的。也就进行不了接下来的操作,所以放弃了。

下面是失利作品:

第二次尝试 :

           设计思路:用字典把物品代号及其重量~价值~单位重量的价值相互对应起来(此时用到zip函数:把两个列表里的数联合到一个字典里),再对物品的单价(我把物品的单价作为字典里的 “ 值 ”,把物品代号作为 “ 键  ”)进行大小排序,经过多次尝试,决定对物品单价进行降序排列,降序排列后,反转键值,便于输出物品代号 来联系三个性质,最后形成的应是物品单价从大到小的的字典,然后从头遍历字典,筛选出满足条件的物品代号与价值总量。

        遇到的问题:

1.    对物品单价字典进行从大到小的排序尝试很多次,不成功,总会出现错误,用的是sorted(iterable,key,reverse) ,主要是里面成分书写或多或少有出入,(其实到现在都没弄懂里面涉及的相关原理), 如下:

z=sorted(dict3.items(),key=lambda item:item[1],reverse=True)  

其中lambda后面的item:item[1]不理解,不过reverse=True表示降序,当为False时,则为降序。

2.当对字典降序排列后,输出的不再是字典,而是

[('C', 5.0), ('H', 5.0), ('F', 4.0), ('B', 1.3333333333333333), ('G', 1.2), ('D', 1.0), ('E', 0.875), ('A', 0.4)]


{列表里包含元组,这又是一个棘手问题,于是又得想办法 把它转化为字典,(经过一段“艰辛和难熬”的百度,终于查出来了)

3.反转键值的格式不会,反转键值后,发现比原来少了数据,是因为变化前的值不唯一,(又难倒我了

变化前:{'C': 5.0, 'H': 5.0, 'F': 4.0, 'B': 1.3333333333333333, 'G': 1.2, 'D': 1.0, 'E': 0.875, 'A': 0.4}
变化后:{5.0: 'H', 4.0: 'F', 1.3333333333333333: 'B', 1.2: 'G', 1.0: 'D', 0.875: 'E', 0.4: 'A'}
 

4.从头遍历新字典的表达方式中的变化量不会写 ,如下标了颜色的:

for value in new_dict.values():
   print(value)

5.条件判断不起作用:我设置了重量和不超过背包容量,才执行价值的汇总,

for value in new_dict.values():
   print(value)
   ww=c+dict1.get(value)
   if ww <= 140:
      s += dict2.get(value)
      print(value, s)
      y1.append(value)
      y2.append(s)
   elif ww>140:
      print("error")
print(y1,y2)

然而它还是一股脑儿全输了出来,

['H', 'F', 'B', 'G', 'D', 'E', 'A'] [15.0, 55.0, 95.0, 125.0, 175.0, 210.0, 220.0]

所以,只能在没超过值的范围内挑选。比如挑175.0以前的H,F,B,G.

第三次尝试:

              设计思路:改进:不用反转键值,直接遍历字典里的键,得到物品代号。(虽然之前绕了很多,不过也绕来了很多知识点,知道有反转键值的操作)这样,就不会出现少数据的情况。

 运行结果:

明显瑕疵:要从结果中挑出前四位才满足背包承载容量内,

                   条件判断不起作用

以下为完整代码:

w = []  # 储存物品重量
v = []  # 储存物品价值
d = []  # 储存物品单位重量的价值
y1 = []
y2=[]
keys = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
for i in range(8):
   l = float(input("please input their weight:"))    # 输入物品重量
   w.append(l)
for i in range(8):
   m = float(input("please input their value:"))     # 输入物品价格
   v.append(m)
for i in range(8):                                   # 求解物品单位重量的价值
   n = v[i] / w[i]
   d.append(n)
print(w)                                             # 连续输出便于观察程序的对错与进行
print(v)
print(d)
dict1 = dict(zip(keys, w))                           # 用字典分别表示物品与其重量 价格 单价的对应关系
dict2 = dict(zip(keys, v))
dict3 = dict(zip(keys, d))
print(dict1)                                          # 连续输出便于观察程序的对错与进行
print(dict2)
print(dict3)
z=sorted(dict3.items(),key=lambda item:item[1],reverse=True)            #对字典降序排列
print(z)
x=dict(z)                                                               # 重新生成字典,
print(x)
s = 0  # 背包里的价值
c = 0  # 背包里的容量
for key in x.keys():
   print(key)
   ww=c+dict1.get(key)
   if ww <= 140:
      s += dict2.get(key)
      print(key, s)
      y1.append(key)
      y2.append(s)
   elif ww>140:
      print("error")
print(y1,y2)

猜你喜欢

转载自blog.csdn.net/qq_44119514/article/details/85312187