贪心算法分数背包问题(小白版)

一、问题提出

已知一个背包容量为 C ,有 n 个物品,其重量和价值分别为(w1,w2,...wn) 和 (v1,v2,...vn),要求将这个n个物品进行装包,使得不超出容量的前提下,背包内物品价值最大。使用贪心策略求解该问题, 体验贪心选择标准对贪心策略的意义。

二、思路分析

  1. 贪心算法是用来解决分数背包问题而非0-1背包问题(更多是动态规划之类)。分数背包问题是物品可以切割,最后一个物品放不下时,可以只放部分;而0-1背包问题是物品不能分割。
  2. 个人理解:贪心算法是贪图眼前利益,只要眼前最大的价值。背包问题的“价值”可以理解为三种——性价比贪心(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

其实是自己做作业走了很多弯路,很想帮到大家。水平不够,有不足之处敬请指正,祝万事顺意!

  • 12
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
分数背包问题是指一个背包有一定的容量,可以装入一些物品,每种物品有重量和价值两个属性。要求在不超过背包容量的前提下,选择一些物品装入背包,使得背包中物品的总价值最大。 贪心算法思路如下: 1. 计算每种物品的单位价值(即每一单位重量所对应的价值)。 2. 按照单位价值从大到小的顺序对物品进行排序。 3. 依次选择单位价值最高的物品放入背包中,直到背包装满或者物品都被选择完为止。 代码实现如下: ``` #include <iostream> #include <algorithm> using namespace std; struct Item { int weight; // 物品重量 int value; // 物品价值 double unitValue; // 单位价值 }; bool cmp(Item a, Item b) { return a.unitValue > b.unitValue; } double fractionalKnapsack(int W, Item arr[], int n) { // 计算每个物品的单位价值 for (int i = 0; i < n; i++) { arr[i].unitValue = (double)arr[i].value / arr[i].weight; } // 按照单位价值从大到小排序 sort(arr, arr + n, cmp); double totalValue = 0; // 背包中物品的总价值 int weightLeft = W; // 剩余的背包容量 for (int i = 0; i < n; i++) { if (weightLeft == 0) { // 背包已经装满 break; } if (arr[i].weight <= weightLeft) { // 当前物品可以完整地装入背包中 totalValue += arr[i].value; weightLeft -= arr[i].weight; } else { // 当前物品只能部分地装入背包中 totalValue += arr[i].unitValue * weightLeft; weightLeft = 0; } } return totalValue; } int main() { int W = 50; // 背包容量 Item arr[] = {{10, 60, 0}, {20, 100, 0}, {30, 120, 0}}; // 物品重量、价值和单位价值 int n = sizeof(arr) / sizeof(arr[0]); double maxValue = fractionalKnapsack(W, arr, n); cout << "背包能装入的最大价值为:" << maxValue << endl; return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值