贪心法-一般背包问题

一般背包问题

问题描述

想象你有一个背包,它最多可以放 M 公斤的物品。你面前有 n 个物品,每个物品的重量是 Wi,如果将这个物品完全放入背包,你将获得 Pi 的收益。

问题目标

你需要决定如何放置这些物品,以便在不超过背包容量的前提下,获得最大的收益。

问题类型

  • 0/1背包问题:每个物品要么完全放入背包,要么完全不放。这种问题无法用贪心法求得最优解。
  • 一般背包问题:物品可以分割,即你可以取物品的一部分放入背包。这种问题可以使用贪心法求得最优解。

解决方法

在一般背包问题中,我们采用贪心法来求解,具体步骤如下:

  1. 计算价值重量比:首先计算每个物品的 价值重量比,即 Pi/Wi
  2. 排序:根据价值重量比,从高到低对物品进行排序。
  3. 选择物品:从价值重量比最高的物品开始,尽可能多地放入背包,直到背包装满或物品用尽。

小注释

  • 一般背包问题允许物品分割,这意味着你可以取物品的一部分来实现最大价值,这与0-1背包问题不同。

特别提示

  • 贪心法在一般背包问题中可以得到最优解,但在0-1背包问题中只能得到近似解。

贪心策略解析

在解决一般背包问题时,我们可以使用不同的贪心策略来尝试获得最大的背包总价值。以下是三种常见的贪心策略:

1. “价值最大”优先

  • 策略描述:选择价值最高的物品优先放入背包。
  • 优势:可以尽可能快地增加背包的总价值。
  • 劣势:可能会导致背包容量消耗过快,从而减少装入背包的物品数量,影响目标函数的最大值。

2. “重量最轻”优先

  • 策略描述:选择重量最轻的物品优先放入背包。
  • 优势:可以装入尽可能多的物品,间接增加背包的总价值。
  • 劣势:背包的价值增长可能不够迅速,因为轻的物品不一定价值高,这可能影响目标函数的最大值。

3. “单位重量价值最大”优先

  • 策略描述:选择单位重量价值最大的物品优先放入背包。
    • 单位重量价值 = 价值 / 重量
  • 优势
    • 在背包价值增长和背包容量消耗之间寻找平衡。
    • 确保在不牺牲背包容量的前提下,尽可能增加背包的总价值。

选择建议

  • 推荐策略:通常选择“单位重量价值最大”优先的策略,因为它综合考虑了价值和重量,更有可能达到目标函数的最大值。

图解
物品的价值和重量如表2-3所示。如果背包容量 w = 50,怎么才能装入最大价值的物品?

物品清单
在这里插入图片描述

(1)贪心策略是每次选单位重量价值(价值/重量)大的物品,因此可以按单位重量价值对物品进行降序排列,排序后的物品清单如下所示:

排序后的物品清单
在这里插入图片描述
(2)按照贪心策略,每次选择单位重量价值大的物品装入背包。

求解步骤

第一次选择

  • 物品3 的单位重量价值最大,为5。
  • 背包容量:50kg > 10kg(物品3的总重量)
  • 操作:物品3全部放入背包。
  • 结果:背包容量剩余50-10=40kg,剩下物品2和物品1未放入。

第二次选择

  • 在物品2和物品1中,物品2 的单位重量价值最大,为4。
  • 背包容量:40kg > 30kg(物品2的总重量)
  • 操作:物品2全部放入背包。
  • 结果:背包容量剩余40-30=10kg,剩下物品1未放入。

第三次选择

  • 考虑物品1,背包容量:10kg < 20kg(物品1的总重量)
  • 操作:最多放一半的物品1(即10kg重量)。
  • 结果:背包容量满,无法再放入更多物品。

总价值计算

  • 物品1 放一半,价值为 60/2= 30
  • 物品2 全部放入,价值为 120
  • 物品3 全部放入,价值为 50

计算公式:
计算总价值
= 60*1/2(物品1放一半)+ 120(物品2全放)+50(物品3全放)
=30+120+50
=200

(3)构造最优解
通过贪心策略,我们成功地将总价值最大化至200,同时不超过背包的容量限制。

代码:

// 定义结构体Item,包含重量weight和价值value
typedef struct {
    float weight; // 物品的重量
    float value;   // 物品的价值
} Item;

// compare函数用于比较两个Item对象,计算每个物品的价值密度(value/weight),并返回值较大的那一个
int compare(const void *a, const void *b) {
    // 解压指针指向的Item对象
    Item *itemA = (Item *)a;
    Item *itemB = (Item *)b;
    // 计算每个物品的密度,进行大小比较
    float ratioA = itemA->value / itemA->weight;
    float ratioB = itemB->value / itemB->weight;
    // 比较结果返回大于、小于0分别代表排序后B应排在A前面
    return (ratioB > ratioA) - (ratioB < ratioA);
}

// 函数Knapsack接收背包容量、物品数组、是否放入的标志数组和总价值输出指针
void Knapsack(int n, float capacity, Item items[], float x[], float *value) {
    *value = 0.0; // 初始化总价值
    // 对物品数组按照价值密度降序排列
    qsort(items, n, sizeof(Item), compare);
    // 遍历每个物品,如果不超过背包容量,则全部放入并更新容量和价值
    for (int i = 0; i < n; i++) {
        if (items[i].weight <= capacity) {
            x[i] = 1; // 标记放入
            *value += items[i].value; // 更新总价值
            capacity -= items[i].weight; // 减少背包容量
        } else { // 如果超过,只取部分放入
            x[i] = capacity / items[i].weight; // 取整数部分放入
            *value += items[i].value * x[i];
            break; // 找到第一个合适的物品后停止
        }
    }
}

// 主函数,获取用户输入,创建并初始化数组,调用Knapsack函数,输出结果,并释放内存
int main() {
    int n; // 物品数量
    float capacity; // 背包容量
    // 用户输入
    printf("请输入背包的容量: ");
    scanf("%f", &capacity);
    printf("请输入物品的数量: ");
    scanf("%d", &n);
    
    // 动态分配存储空间
    Item *items = (Item *)malloc(n * sizeof(Item));
    float *x = (float *)malloc(n * sizeof(float));
    
    // 输入每个物品的信息
    for (int i = 0; i < n; i++) {
        printf("请输入物品 %d 的重量: ", i + 1);
        scanf("%f", &items[i].weight);
        printf("请输入物品 %d 的价值: ", i + 1);
        scanf("%f", &items[i].value);
    }

    // 调用Knapsack函数求解
    float value;
    Knapsack(n, capacity, items, x, &value);

    // 输出结果
    printf("最大价值: %f\n", value);
    printf("物品选择: ");
    for (int i = 0; i < n; i++) {
        printf("%d ", (int)(x[i] * 10)); // 将浮点数转换为整数并保留一位小数显示
    }
    printf("\n");
    
    // 释放动态分配的内存
    free(items);
    free(x);
    
    return 0; // 程序结束
}
  • 13
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 0-1背包问题是一个经典的组合优化问题,其目标是在限定的背包容量下,选择一组物品放入背包中,使得背包中物品的总价值最大化。 贪心法是一种求解0-1背包问题的常用方法。其基本思想是每次选择当前最有利的物品放入背包中,直至背包容量不足或所有物品都放入背包为止。 具体实现贪心法0-1背包问题c的步骤如下: 1. 将所有物品按照单位重量的价值从大到小进行排序; 2. 初始化背包容量剩余空间为背包的总容量,初始化背包的总价值为0; 3. 依次遍历排序后的物品列表,对于每个物品: - 如果物品重量小于等于背包剩余空间,则将该物品放入背包中,背包剩余空间减少该物品重量,背包总价值增加该物品价值; - 如果物品重量大于背包剩余空间,则终止循环; 4. 返回背包中的物品总价值作为结果。 贪心法0-1背包问题c的时间复杂度为O(nlogn),其中n为物品数量,主要消耗时间的操作是对物品列表的排序。 ### 回答2: 贪心法是一种常用的求解最优问题的算法,包括0-1背包问题。在0-1背包问题中,我们有一系列物品,每个物品有重量和价值两个属性。我们需要选择一些物品放入背包,使得背包的总重量不超过背包的容量,同时能够使得背包中物品的总价值最大化。 贪心法的思想是每次选择当前最有利于解的选择,即每次选择重量最小但价值最高的物品放入背包。具体步骤如下: 1. 根据物品的重量和价值计算每个物品的价值密度(即单位重量下的价值)。 2. 将物品按照价值密度从高到低排序。 3. 依次选择物品放入背包,直到背包的重量达到限制或者所有物品都已经放入背包。 4. 计算放入背包的物品的总价值。 贪心法的优点是简单高效,时间复杂度较低。然而,贪心法并不保证能够得到最优解。在某些情况下,使用贪心法得到的结果可能与动态规划等其他算法得到的结果不一致。 对于0-1背包问题c,我们可以使用贪心法求解。具体步骤如下: 1. 计算每个物品的价值密度,即价值除以重量。 2. 按照价值密度从高到低对物品进行排序。 3. 依次选择物品放入背包,直到背包的重量达到限制或者所有物品都已经放入背包。 4. 最后计算放入背包的物品的总价值。 需要注意的是,虽然贪心法在某些情况下可能得到次优解,但在某些特殊的条件下,贪心法却可以得到最优解。因此,在实际应用中,根据具体问题的特点选择合适的算法是很重要的。 ### 回答3: 0-1背包问题是一个经典的动态规划问题,目标是在有限容量的背包中选择若干个物品放入背包,使得物品的总价值最大化。而贪心法无法解决0-1背包问题的最优解。 贪心法是一种贪婪的策略,每次选择当前看起来最好的解决方案。但在0-1背包问题中,贪心法会导致错误的结果。例如,假设有三个物品A、B和C,分别占据1、4和3的容量,价值分别为2、5和4,而背包的容量为4。若采用贪心法,首先选择B放入背包,然后剩余容量为0,无法再放入其他物品,总价值为5。但实际上,最优解应该是选择A和C,总价值为6。 因此,为了解决0-1背包问题,需要采用动态规划的方法。动态规划通过将问题划分为子问题,并保存子问题的解,最后通过组合子问题的解得到原问题的最优解。对于0-1背包问题,可以使用一个二维数组dp来保存子问题的解,其中dp[i][j]表示在前i个物品中,容量为j的背包可以获得的最大价值。通过迭代计算dp数组,最后得到dp[n][C]即为问题的最优解。 综上所述,贪心法无法解决0-1背包问题的最优解,需要采用动态规划的方法来求解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值