背包问题【算法 07】

news2024/9/22 13:37:24

背包问题

请添加图片描述

背包问题是经典的计算机科学问题之一,涉及到如何在有限资源的约束下,选择最优的物品组合,以最大化收益。这个问题在现实中有广泛的应用,例如资源分配、物流调度和投资组合优化等。本文将详细介绍背包问题的定义、解决方法、常见变种,以及通过C语言的实现来帮助理解。

1. 背包问题简介

背包问题最早由丹麦数学家 Knuth 提出,其核心思想是:在一组物品中,每个物品都有一定的价值和重量,在背包的容量有限的前提下,选择哪些物品可以使得背包内物品的总价值最大化。

1.1 问题定义

背包问题可以表述为:给定 n 个物品,每个物品 i 有一个重量 w_i 和价值 v_i,在背包最大承重量 W 限制下,如何选择物品,使得装入背包的物品总重量不超过 W,且总价值最大。

公式化的定义如下:

  • 物品集合:{1, 2, ..., n}
  • 每个物品的重量:w_i
  • 每个物品的价值:v_i
  • 背包的容量:W

目标:在满足背包容量的前提下,最大化价值和:

max ∑ i = 1 n v i x i \text{max} \sum_{i=1}^n v_i x_i maxi=1nvixi
其中,x_i 表示第 i 个物品是否被选中,x_i = 1 表示选择,x_i = 0 表示不选择。

1.2 背包问题的类型

背包问题有多个变种,常见的类型包括:

  1. 0-1 背包问题:每个物品要么选择(1),要么不选择(0)。
  2. 完全背包问题:每个物品可以被选择多次。
  3. 分数背包问题:可以选择物品的一部分。

2. 0-1 背包问题

最经典的背包问题是 0-1 背包问题,即每个物品只能被选一次。此问题的解法包括动态规划(DP)和回溯法等。

2.1 动态规划解法

动态规划是一种自底向上的解决问题的方式,它可以有效地解决背包问题,避免暴力搜索带来的指数级时间复杂度。基本思想是构建一个二维数组 dp[i][w],表示前 i 个物品中能够装入重量为 w 的背包的最大价值。

2.1.1 状态转移方程

dp[i][w] 表示前 i 个物品能够放入容量为 w 的背包中的最大价值,则状态转移方程为:

d p [ i ] [ w ] = { d p [ i − 1 ] [ w ] , w < w i max ⁡ ( d p [ i − 1 ] [ w ] , d p [ i − 1 ] [ w − w i ] + v i ) , w ≥ w i dp[i][w] = \begin{cases} dp[i-1][w], & w < w_i \\ \max(dp[i-1][w], dp[i-1][w-w_i] + v_i), & w \geq w_i \end{cases} dp[i][w]={dp[i1][w],max(dp[i1][w],dp[i1][wwi]+vi),w<wiwwi
该方程表示如果当前物品 i 的重量超过背包剩余容量,则不能选择该物品;否则,我们可以选择不放入该物品或者放入该物品,取两者中的最大值。

2.2 动态规划算法实现

以下是 0-1 背包问题的 C 语言实现:

#include <stdio.h>

// 定义最大物品数量和最大背包容量
#define MAX_ITEMS 100
#define MAX_WEIGHT 1000

// 动态规划求解0-1背包问题
int knapsack(int n, int W, int weights[], int values[]) {
    int dp[MAX_ITEMS+1][MAX_WEIGHT+1] = {0};

    // 遍历每个物品
    for (int i = 1; i <= n; i++) {
        for (int w = W; w >= weights[i-1]; w--) {
            dp[i][w] = dp[i-1][w];
            if (w >= weights[i-1]) {
                dp[i][w] = (dp[i-1][w] > dp[i-1][w - weights[i-1]] + values[i-1]) ? dp[i-1][w] : (dp[i-1][w - weights[i-1]] + values[i-1]);
            }
        }
    }

    return dp[n][W];
}

int main() {
    int n = 5;  // 物品数量
    int W = 10; // 背包容量
    int weights[] = {2, 2, 6, 5, 4};  // 每个物品的重量
    int values[] = {6, 3, 5, 4, 6};   // 每个物品的价值

    int maxValue = knapsack(n, W, weights, values);
    printf("背包可以装入的最大价值为: %d\n", maxValue);

    return 0;
}

2.3 案例分析

假设有5个物品,重量分别为 {2, 2, 6, 5, 4},价值分别为 {6, 3, 5, 4, 6},背包容量为 10

通过动态规划方法,得到的最大价值为 15。这是通过选择第 1、第 2 和第 5 个物品实现的,它们的总重量为 2+2+4=8,总价值为 6+3+6=15

3. 完全背包问题

在完全背包问题中,每个物品可以选择多次。这与 0-1 背包问题的区别在于,状态转移方程发生了变化,因而求解方法也有所不同。

3.1 状态转移方程

对于完全背包问题,状态转移方程为:

d p [ i ] [ w ] = max ⁡ ( d p [ i − 1 ] [ w ] , d p [ i ] [ w − w i ] + v i ) dp[i][w] = \max(dp[i-1][w], dp[i][w-w_i] + v_i) dp[i][w]=max(dp[i1][w],dp[i][wwi]+vi)
与 0-1 背包不同,选择当前物品时,需要考虑放入多次的情况。

3.2 完全背包问题的C语言实现

#include <stdio.h>

int knapsack_complete(int n, int W, int weights[], int values[]) {
    int dp[MAX_WEIGHT+1] = {0};

    for (int i = 0; i < n; i++) {
        for (int w = weights[i]; w <= W; w++) {
            dp[w] = (dp[w] > dp[w - weights[i]] + values[i]) ? dp[w] : dp[w - weights[i]] + values[i];
        }
    }

    return dp[W];
}

int main() {
    int n = 5;
    int W = 10;
    int weights[] = {2, 2, 6, 5, 4};
    int values[] = {6, 3, 5, 4, 6};

    int maxValue = knapsack_complete(n, W, weights, values);
    printf("完全背包可以装入的最大价值为: %d\n", maxValue);

    return 0;
}

3.3 案例分析

在同样的物品和背包容量情况下,完全背包的最大价值是 18,因为在完全背包中,可以选择物品 15 多次,从而获得更高的价值。

4. 分数背包问题

分数背包问题允许将物品分割开来,也就是说可以选择物品的一部分。通常使用贪心算法解决该问题,依据单位重量的价值(价值/重量)对物品进行排序,然后依次选择单位价值最高的物品直到背包满为止。

4.1 分数背包问题的案例分析

在分数背包问题中,物品可以被部分选择,这与 0-1 背包和完全背包不同。例如,假设有以下 3 个物品:

  • 物品 1:重量 10,价值 60
  • 物品 2:重量 20,价值 100
  • 物品 3:重量 30,价值 120

背包的容量为 50。我们可以计算每个物品的单位重量价值,即 value/weight 比率:

  • 物品 1:60/10 = 6
  • 物品 2:100/20 = 5
  • 物品 3:120/30 = 4

根据贪心算法的思想,应该优先选择单位价值最高的物品。具体过程如下:

  1. 选择物品 1(单位价值最高,6),将其全部放入背包。此时,背包剩余容量为 50 - 10 = 40,价值为 60
  2. 选择物品 2(单位价值次高,5),将其全部放入背包。此时,背包剩余容量为 40 - 20 = 20,价值增加为 60 + 100 = 160
  3. 选择物品 3(单位价值最低,4),但此时背包只剩下 20 的空间,因此只能选择物品 3 的一部分,价值为 120 * (20/30) = 80

因此,最终的最大价值为 60 + 100 + 80 = 240

4.2 分数背包问题的 C 语言实现

以下是分数背包问题的 C 语言实现:

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int weight;
    int value;
    double ratio;
} Item;

// 比较函数,用于根据单位价值对物品排序
int cmp(const void* a, const void* b) {
    return ((Item*)b)->ratio > ((Item*)a)->ratio ? 1 : -1;
}

// 贪心算法解决分数背包问题
double fractional_knapsack(int n, int W, Item items[]) {
    // 根据单位价值排序
    qsort(items, n, sizeof(Item), cmp);
    double maxValue = 0.0;

    // 贪心地选择物品
    for (int i = 0; i < n && W > 0; i++) {
        if (W >= items[i].weight) {
            W -= items[i].weight;
            maxValue += items[i].value;
        } else {
            maxValue += items[i].value * ((double)W / items[i].weight);
            W = 0;
        }
    }

    return maxValue;
}

int main() {
    int n = 3;  // 物品数量
    int W = 50; // 背包容量

    Item items[] = {
        {10, 60, 6.0},
        {20, 100, 5.0},
        {30, 120, 4.0}
    };

    double maxValue = fractional_knapsack(n, W, items);
    printf("分数背包可以获得的最大价值为: %.2f\n", maxValue);

    return 0;
}

4.3 分析与总结

通过贪心算法,我们可以有效解决分数背包问题。该算法的时间复杂度主要由排序决定,为 O(n log n),而解决过程为 O(n)。与动态规划相比,贪心算法的效率更高,但它只能用于分数背包问题,不能用于 0-1 背包问题。

5. 不同背包问题的比较

问题类型可选物品算法类型时间复杂度备注
0-1 背包问题每个物品只能选择一次动态规划O(nW)经典问题
完全背包问题每个物品可以选择多次动态规划O(nW)物品可多次选择
分数背包问题物品可以部分选择贪心算法O(n log n)需要排序

6. 背包问题的现实应用

背包问题在许多实际应用中有广泛的应用,例如:

  1. 资源分配问题:在有限的资源下,如何分配资源以最大化收益。
  2. 物流调度问题:如何在有限的运输空间内安排货物的装载,使得运输效益最大化。
  3. 投资组合问题:如何在有限的资金下,选择合适的投资组合以最大化收益。

背包问题的灵活性使得它可以应用于各种优化问题中,不仅仅局限于物理背包的装载问题。

7. 总结

背包问题作为组合优化中的经典问题,有多种变体和解决方法。本文详细介绍了 0-1 背包问题、完全背包问题和分数背包问题的概念、算法及实现,并通过案例分析加深对这些问题的理解。通过不同的解法,解决了物品选择的最大价值问题,这种优化思想在现实中有广泛的应用价值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2068783.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pytorch深度学习基础 7(简单的的线性训练,SGD与Adam优化器)

接下来小编来讲一下一些优化器在线性问题中的简单使用使用&#xff0c;torch模块中有一个叫optim的子模块&#xff0c;我们可以在其中找到实现不同优化算法的类 SGD随机梯度下降 基本概念 定义&#xff1a;随机梯度下降&#xff08;SGD&#xff09;是一种梯度下降形式&#…

mysql中出现错误1138-Invalid use of NULL value

问题&#xff1a;1138-Invalid use of NULL value 解决&#xff1a; 问题是当前字段中&#xff0c;有null的值&#xff0c;简单来说就是&#xff0c;你表里有空值&#xff0c;不能设置不为空&#xff01;&#xff01;&#xff01; 把空的值删掉重新设计就好了

第一次重大人工智能失败刚刚发生

这终于发生了。我们迎来了第一家真正意义上的 AI 公司惨败。 Inflection是一家由比尔盖茨、埃里克施密特、微软等人投资的公司&#xff0c;它成为第一家被冲进马桶的生成式人工智能相关公司。 他们最重要的产品是Pi&#xff0c;ChatGPT 的竞争对手&#xff0c;专注于成为友好且…

SpringAOP使用详解

AOP使用详解 首先创建maven项目 添加依赖在pom.xml里 创建三层结构和spring.xml文件&#xff0c;只要用到注解就得写扫描包在spring.xml里 上篇文章的知识点总结 对上篇文章excution详细解释 如果把前置通知修改成这个代表只有带有Logger注解的才会生效 合并注解的方法用&…

Windows权限维持实战

目录 介绍步骤 介绍 在攻击过程中中对于拿到的shell或钓上来的鱼&#xff0c;目前比较流行用CS做统一管理&#xff0c;但实战中CS官方没有集成一键权限维持的功能&#xff0c;为了将该机器作为一个持久化的据点&#xff0c;种植一个具备持久化的后门&#xff0c;从而随时可以连…

ffmpeg最新5.1.6版本源码安装

一、编译安装需要的开源编码格式&#xff1a; 首先在编译安装这些开源编码格式之前,我们要明白为啥需要他们&#xff1a; aacx264x265 为啥需要呢&#xff1f;如果你对ffmpeg稍微了解的话&#xff0c;ffmpeg本身是一个框架&#xff0c;自身默认并没有支持这三种编码格式&…

Vue3 后台管理系统项目 前端部分

这里写目录标题 1 创建Vue3项目1.1 相关链接1.2 Vue Router1.3 Element1.4 scss1.5 mitt1.6 axios1.7 echarts1.8 配置vite.config.js 2 CSS部分2.1 样式穿透2.2 :style &#xff1a;在样式中使用插值语法 3. ElementUI3.1 rules&#xff1a; 数据验证3.2 修改element.style中的…

《计算机网络-期末模拟卷》

一、分析题&#xff08;每题 6 分&#xff0c;共 36 分&#xff09; 1.请分析图 1-1 所展示的是哪种互联网接入技术&#xff0c;分析此接入技术的优势&#xff0c;并介绍你所了解的其他互联网接入技术。&#xff08;至少写 3 个&#xff09; 二、计算问答题&#xff08;每题 5…

docker应用

打包传输 1.将镜像打包 #查看帮助文件 docker --help #找到save&#xff0c;可以将镜像保存为一个tar包 docker save --help #查看save使用方式 #查看现有的镜像 docker images # docker save --output centos.tar centos:latest ls ...centos.tar... 可以将tar发送给其他用户…

JS基础进阶3-DOM事件

DOM事件流 一、定义 DOM事件流指的是从页面接收事件的顺序。这个路径包括了事件的捕获阶段、目标阶段和冒泡阶段。 图片来源黑马pink老师PPT 二、事件流阶段 DOM事件流涉及三个主要阶段&#xff1a; 捕获阶段&#xff08;Capturing Phase&#xff09;&#xff1a; 事件从…

QtChart1-基础入门

Qt Charts概述 Qt Charts模块是一组易于使用的图标组件&#xff0c;它基于Qt的Graphice View架构&#xff0c;其核心组件是QChartView和QChart。QChartView的父类是QGraphicsView&#xff0c;就是Graphics View架构中的视图组件&#xff0c;所以&#xff0c;QChartView是用于显…

Apollo9.0 PNC源码学习之Planning模块—— Lattice规划(六):横纵向运动轨迹评估

参考文章: (1)Apollo6.0代码Lattice算法详解——Part6:轨迹评估及碰撞检测对象构建 (2)自动驾驶规划理论与实践Lattice算法详解 0 前言 横纵向运动轨迹的评估,主要通过构建定点巡航和定点停车两个场景下,对纵向运动参考速度、加速度、加加速度的大小进行检验和过滤,然…

Vue3源码调试-第一篇

前言 相信大家在前端从业生涯中都会被问到过&#xff0c;有了解过Vue源码吗&#xff1f;我是没有的&#xff0c;所以今天就来读好吧&#xff0c;浅浅读一下&#xff0c;顺便记录一下。 那究竟怎么读&#xff0c;从哪里读&#xff0c;我就不啰嗦了&#xff0c;直接给大家一个链…

python dash框架 油气田可视化软件设计文档

V1.1:机器学习框架(神经网络) 时间范围优化 表格布局优化 添加前端设计元素布局 V1.0&#xff1a;基础布局和对应计算函数 要求 首先第一部分是通过神经网络预测天然气流量&#xff0c;其中输入开始时间和截止时间是为了显示这一段时间内的天然气流量预测结果 第二部分&…

小型空气净化器可以除猫毛吗?宠物空气净化器评测推荐

前段时间我有个病人&#xff0c;诊断出来肺结节&#xff0c;他第一反应就是说他家养着好几只猫&#xff0c;会不会是吸入宠物毛发导致的肺结节。有些结节确实跟宠物有关系&#xff0c;如果是对毛发过敏、或者是对排泄物过敏&#xff0c;养宠物就会增加患结节的概率。不过就算是…

推荐一款AI智能编程助手CodeGeeX

最近&#xff0c;使用了一款AI智能编程助手CodeGeeX&#xff0c;感觉还不错&#xff0c;推荐给大家。 官网地址&#xff1a;https://codegeex.cn/ 一、安装教程 IDEA中安装插件&#xff1a;https://codegeex.cn/downloadGuide#idea VSCode中安装插件&#xff1a;https://codege…

八股(5)——数据库

八股&#xff08;5&#xff09;——数据库 4.1 数据库基础什么是数据库, 数据库管理系统, 数据库系统, 数据库管理员?什么是元组, 码, 候选码, 主码, 外码, 主属性, 非主属性&#xff1f;主键和外键有什么区别?为什么不推荐使用外键与级联&#xff1f;什么是 ER 图&#xff1…

TD学习笔记————中级教程总结(中)

目录 四、生成艺术 问题: CHOP TO放置后直接报错 附着不上线 五、Python Lists 与 Python Dictionaries 问题: 使用for的格式要求 显示numRows错误 List中表格定义报错 六、Replicate 与 Instance 问题: 传递处理好的噪音后不变化 Renderpass区分线和字时不起作用…

安科瑞智能物联网关:重塑能源管理新纪元,远程智控尽在“掌”握

在数字化转型浪潮中&#xff0c;能源管理与工业自动化领域正经历着前所未有的变革。安科瑞智能物联网关-智能通信管理机不仅重新定义了智能监控与保护装置的通信管理模式&#xff0c;更为能源数据采集与远程控制提供了前所未有的高效解决方案。 安科瑞智能物联网关&#xff0c…

qt开发环境搭建Qt Creator并创建Demo项目

一 Qt Creator工具下载&#xff1a;工具下载链接&#xff1a; Index of /archive/online_installers/4.8 (qt.io) 下载后点击安装&#xff0c;没有账号得先注册一个账号&#xff0c;如下图&#xff0c;然后点击下一步 随便填点&#xff0c;我填"abc"&#xff0c;然…