首页 > 其他分享 >贪心法-一般背包问题

贪心法-一般背包问题

时间:2024-08-12 23:23:24浏览次数:16  
标签:背包 重量 value 一般 物品 价值 放入 贪心

一般背包问题

问题描述

想象你有一个背包,它最多可以放 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; // 程序结束
}

标签:背包,重量,value,一般,物品,价值,放入,贪心
From: https://blog.csdn.net/qq_52291558/article/details/141112073

相关文章

  • Day27 贪心算法part1
    任务455.分发饼干假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子i,都有一个胃口值g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干j,都有一个尺寸s[j]。如果s[j]>=g[i],我们可以将这个饼干j分配给孩子i,这......
  • 背包问题 持续更新中!
    背包九讲一、01背包问题题目概览有NNN件物品和一个容量是VVV的背......
  • 动态规划之——背包DP(进阶篇)
    文章目录概要说明多重背包(朴素算法)模板例题思路code多重背包(二进制优化)模板例题思路code多重背包(队列优化)模板例题思路混合背包模板例题思路code1code2二维费用背包模板例题思路code概要说明本文讲多重背包、混合背包以及二维费用背包,至于其他背包问题后续......
  • 贪心
    有些题dp是难做的或做不了的,这个时候可以去设计一种策略使得决策尽可能最优,也就是贪心。可以说贪心有时候是一种乱搞,但乱搞也能搞出出题人想不到的正解。反悔贪心有些题中直接dp的复杂度很高并且很难优化或者有后效性无法dp,朴素贪心考虑可以做到\(O(n)\)但是无法保证正确......
  • 贪心系列专题篇四
    目录整数替换解法一解法二俄罗斯套娃信封问题堆箱子可被三整除的最大和距离相等的条形码重构字符串声明:接下来主要使用贪心法来解决问题!!!整数替换题目思路下面将使用两种方法来解决这道题,第一种方法是递归+记忆化搜索;第二种方法是贪心。解法一使用递归+记忆......
  • 低代码: 系统开发准备之确定一般开发流程,需求分析,复杂度分析,标准开发流程
    概述低代码系统开发之前,我们首先要进行一些准备我们首先知道我们软件开发的一般流程同时,我们还要知道,我们整个系统平台的需求如何之后,我们要基于需求进行设计,包含UI设计与系统架构设计一般开发流程系统开发一般要经过如下几个步骤,低代码系统平台也不例外我们来看下软件开......
  • 一定要听劝!网络安全这玩意儿真不是一般人能学的!
    我是一名5年半的网安工程师“老司机”,要给准备入坑网络安全的同学泼盆冷水了,网络安全真的不是一般人能学的。我作为一名网安老司机,为什么要给大家泼冷水?好多人说:网安基础很简单,是个人稍微认真点都能懂,给网安打上了简单、易懂的标签。然后上来就是一波言论浮夸的输出,把一些......
  • 贪心算法-活动安排问题
    贪心算法贪心算法总是选择当前看起来最优的选择(局部最优解),希望得到的结果是一个整体最优解。但是,并非总是选择局部最优解就能够得到整体最优解,这一点需要在问题具有贪心选择性和优化子结构时才成立。贪心选择性贪心选择性:第一次做出贪心选择是正确的。优化子结构问题......
  • 「Day 2—贪心问题&分治&前缀和」
    贪心问题定义顾名思义,越贪越好。。。习题P1094[NOIP2007普及组]纪念品分组思路简单来说:最少的+最多的,利用双指针。代码#include<algorithm>#include<iostream>usingnamespacestd;intw,n;intp[30005];intmain(){cin>>w;cin>>n;for(inti=1;i......
  • 动态规划之——背包DP(入门篇)
    文章目录概要说明01背包模板例题题意概要思路code1code201背包的应用题题目来源思路code完全背包模板例题题意概要思路code概要说明本文只讲了01背包和完全背包,至于其他背包问题后续补充01背包模板例题点击这里题意概要思路01背包的模板题首先对于背包问......