C语言动态规划解决0-1背包问题

动态规划(Dynamic Programming,简称DP)是一种在数学、计算机科学和经济学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划常常适用于有重叠子问题和最优子结构性质的问题,它能够将问题分解为相互独立的子问题,并将子问题的解存储起来,以便下次需要时直接使用,从而减少计算量,提高效率。最经典的例子就是0-1背包问题。

0-1背包问题描述:给定一组物品,每种物品都有自己的重量和价值,在限定的总重量内,选取若干种物品,使得物品的总价值最大。其中,每种物品只能选择一次或不选择。
 

基本思路

用子问题定义状态:f[i][c] 表示前 i 件物品放入一个容量为 c 的背包可以获得的最大价值。第 i 件物品的重量是 wi,价值是 vi,则其状态转移方程是:

f[i][c] = max(f[i-1][c], f[i-1][c-wi] + vi)

这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生出来的。分析子问题“将前 i 件物品放入容量为 c 的背包中”,考虑第 i 件物品放或不放入背包,可以转化为一个只牵扯前 i-1 件物品的问题:如果不放第 i 件物品,那么问题就转化为“前 i-1 件物品放入容量为 c 的背包中”,价值为 f[i-1][c];如果放第 i 件物品,那么问题就转化为“前 i-1 件物品放入剩下的容量为 c-wi 的背包中”,此时能获得的最大价值就是 f[i-1][c-wi] 再加上通过放入第 i 件物品获得的价值 vi。所以按照这个方程递推完毕后,最终的答案一定是 f[i][c]。


示例程序

#include <stdio.h>#define max(a, b) a > b ? a : bint knapsack(int weights[], int values[], int capacity, int n) {// f[i][c] 表示在前i个物品中选择若干个物品放入容量为c的背包中所能获得的最大价值int f[n + 1][capacity + 1];for (int i = 0; i <= n; i++) {for (int c = 0; c <= capacity; c++) {if (i == 0 || c == 0) {// 前0个物品,或者容量为0,价值也为0f[i][c] = 0;} else if (c < weights[i-1]) {// i表示前i个物品,所以第i个物品的重量是 weights[i-1],对应前面公式中的 wi// 遍历到当前容量c小于当前物品的重量,无法放入该物品,保持背包现状// 即:上一轮遍历物品的循环中同样数量物品的最大价值,所以是 f[i-1][c]f[i][c] = f[i-1][c];} else {// i表示前i个物品,所以第i个物品的价值是 values[i-1],对应前面公式中的 vi// 可以放入,判断放入该物品是否能使背包中物品价值最大// 如果放入,可能需要腾出背包中同样重量的物品,所以是 f[i-1][c-weights[i-1]]// 然后 f[i-1][c-weights[i-1]] + values[i-1] 得到放入该物品后的价值// 不放入该物品(保持背包现状),与放入该物品,取两者中的最大值f[i][c] = max(f[i-1][c], f[i-1][c - weights[i-1]] + values[i-1]);}}}// 输出动态规划数组中的值,显示规划过程,用于分析理解for (int i = 0; i <= n; i++) {for (int c = 0; c <= capacity; c++) {printf("%d", f[i][c]);if (c < capacity) {printf(", ");}}printf("\n");}return f[n][capacity];
}int main() {// 物品重量int weights[] = {2, 2, 1, 3};// 物品价值int values[] = {4, 2, 3, 6};// 背包的容量限制int capacity = 3;// 物品数量int n = sizeof(values) / sizeof(values[0]);printf("最大价值: %d\n", knapsack(weights, values, capacity, n));return 0;
}

 

分析过程

程序输出如下:

0, 0, 0, 0 
0, 0, 4, 4 
0, 0, 4, 4 
0, 3, 4, 7 
0, 3, 4, 7 
最大价值: 7

上面输出的前5行是动态规划数组中的内容,回顾一下程序中的这行注释内容:f[i][c] 表示在前 i 个物品中选择若干个物品放入容量为 c 的背包中所能获得的最大价值。咱们的示例数据中,一共有4个物品,背包的容量为3,所以数组的大小是5x4(为什么维度比物品数和背包容量都大1?请带着这个问题往下看)。现在开始逐行分析数组中的数据:

第1行:不选择任何物品,所以价值都为0。为方便阅读,避免频繁上下滑动屏幕,后续会复制所需查看的输出:

0, 0, 0, 0

第2行:选择前1个物品,该物品重量为2,价值为4。从0-3遍历背包容量,依次尝试放入该物品,遍历过程中,容量为0都不能放入,所以第1列数据永远为0。容量为1不能放入当前物品,容量为2和3可以放入,且是第1个物品,可直接放入背包,得到背包中物品的价值就是该物品的价值4。

0, 0, 0, 0 
0, 0, 4, 4

第3行:选择前2个物品,问题转变为在前1个物品的基础上,放入第2个物品。根据前面的子问题说明:考虑第 i 件物品放或不放入背包,可以转化为一个只牵扯前 i-1 件物品的问题:如果不放第 i 件物品,那么问题就转化为“前 i-1 件物品放入容量为 c 的背包中”,价值为 f[i-1][c];如果放第 i 件物品,那么问题就转化为“前 i-1 件物品放入剩下的容量为 c-wi 的背包中”,此时能获得的最大价值就是 f[i-1][c-wi] 再加上通过放入第 i 件物品获得的价值 vi。第2个物品的重量为2,价值为2。和前一个物品一样,容量为2和3可以放入,但背包剩余容量不够,需要置换前面放入的物品。如果放入该物品,置换出原价值为4的物品,所能得到的价值为2,小于背包中物品现有的价值4,因此不放入该物品,背包中物品价值仍为4。

0, 0, 0, 0 
0, 0, 4, 4 
0, 0, 4, 4

第4行:选择前3个物品,问题转变为在前2个物品的基础上,放入第3个物品。第3个物品的重量为1,价值为3。从0-3遍历背包容量,容量为1时,背包中没有物品,直接放入,背包中物品价值为该物品的价值3;容量为2时,由于已经放入物品,价值为4,该物品可以放入背包,但背包剩余容量不够,需要置换前面放入的物品,置换后所能得到的价值为2,小于背包中物品现有的价值4,因此不放入该物品,背包中物品价值仍为4;容量为3时,背包中有物品,也有剩余容量,根据前面的方程 f[i][c] = max(f[i-1][c], f[i-1][c-wi] + vi) 计算 f[3][3] = max(f[2][3], f[2][3-1]+3),即不放入该物品时的价值 f[2][3] = 4,与放入该物品时的价值 f[2][2]+3 = 7,因此放入该物品,背包中物品价值最大为7。

0, 0, 0, 0 
0, 0, 4, 4 
0, 0, 4, 4 
0, 3, 4, 7

第5行:选择前4个物品,问题转变为在前3个物品的基础上,放入第4个物品。第4个物品的重量为3,价值为6。从0-3遍历背包容量,只有容量为3时可以放入该物品,如果放入该物品,置换出背包中容量为3的物品,f[i-1][c-wi] + vi = f[3][0]+6 = 6,小于不放入该物品时背包中的物品价值7,因此不放入该物品。

0, 0, 0, 0 
0, 0, 4, 4 
0, 0, 4, 4 
0, 3, 4, 7 
0, 3, 4, 7

最终的答案是 f[4][3],程序输出最大价值: 7。

 

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

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

相关文章

搭建神经网络(torch.nn的用法)

零零碎碎总结了一些torch框架里面nn模块的用法&#xff0c;尤其是关于搭建神经网络的 nn.ModuleList nn.Module nn.Sequential nn.Linear nn.Dropout nn.Embedding nn.DataParallel() 将模型封装起来&#xff0c;便于在多个gpu上并行计算&#xff0c;训练或者推理 nn.…

MyBatis-Plus--在xml中使用wrapper的方法

原文网址&#xff1a;MyBatis-Plus--在xml中使用wrapper的方法_IT利刃出鞘的博客-CSDN博客 简介 本文介绍MyBatis-Plus如何在xml中使用wrapper。 Service QueryWrapper<T> wrapper new QueryWrapper<T>(); wrapper.eq("r.room_id", vo.getRoomId())…

量子计算和量子通信技术:引领潜力无限的未来

近年来&#xff0c;随着量子计算和量子通信技术的迅速发展&#xff0c;它们在各个领域的广泛应用前景引起了人们的极大兴趣。本文将深入探讨量子计算和量子通信技术的普遍应用&#xff0c;以及它们预示的未来&#xff0c;同时提出业内人士需要注意的事项。 介绍&#xff1a;量子…

Delphi TCP服务端监听端口获取客户端RFID网络读卡器上传的刷卡数据

本示例使用设备介绍&#xff1a;液显WIFI无线网络HTTP协议RFID云读卡器可编程实时可控开关TTS语-淘宝网 (taobao.com) unit Unit1;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, ComCtrls, ScktComp, StdCtrls, ScktCom…

《数字图像处理-OpenCV/Python》连载(41)图像的旋转

《数字图像处理-OpenCV/Python》连载&#xff08;41&#xff09;图像的旋转 本书京东优惠购书链接&#xff1a;https://item.jd.com/14098452.html 本书CSDN独家连载专栏&#xff1a;https://blog.csdn.net/youcans/category_12418787.html 第 6 章 图像的几何变换 几何变换分…

WordPress Modown 6.2付费下载资源/付费查看内容 wp主题模板+erphpdown11.7

模板简介&#xff1a; 自适应响应式设计&#xff0c;兼容主流浏览器 网格样式与瀑布流样式任意切换 内置SEO优化 自带与主题UI完美兼容搭配的erphpdown前端用户中心页面&#xff08;此功能若单独找我们定制也需要几百&#xff09; 收费付费下载资源、付费查看内容、付费观看…

【华为OD机试AB高分必刷题目】简单的最短路径(Python-Dijkstra算法实现)

🚀你的旅程将在这里启航!本专栏所有题目均包含优质解题思路,高质量解题代码,详细代码讲解,助你深入学习,高分通过! 文章目录 【华为OD机试AB必刷题目】简单的最短路径(Python实现)题目描述解题思路Python题解代码代码OJ评判结果代码讲解寄语【华为OD机试AB必刷题目】…

drawio连接线的样式设置

drawio是一款强大的图表绘制软件&#xff0c;支持在线云端版本以及windows, macOS, linux安装版。 如果想在线直接使用&#xff0c;则直接输入网址draw.io或者使用drawon(桌案), drawon.cn内部完整的集成了drawio的所有功能&#xff0c;并实现了云端存储&#xff0c;以及在线共…

vue3 使用element plus 打包时 报错

vue3vitetselementPlus中运行正常打包出错 能正常运行&#xff0c;但是打包出错 解决打包时出现导入element plus相关的爆红&#xff0c;导致无法打包的问题 如若出现类似于&#xff1a;Module ‘“element-plus”’ has no exported member ‘ElMessage’. Did you mean to…

Python语法基础(字符串 列表 元组 字典 集合)

目录 字符串(str)字符串的创建特殊情况字符串的转义字符字符串的运算符字符串常用方法求字符串长度去掉多余空格是否包含某子串分割字符串合并字符串替换字符串统计统计字符串出现的次数 练习&#xff1a;判断字符串是否为回文串 列表(list)列表的创建列表常用方法遍历列表列表…

金字塔原理小节

目录 第1章 为什么要用金字塔结构 一、归类分组&#xff0c;将思想组织成金字塔 二、奇妙的数字“7” 三、归类分组搭建金字塔 四、找出逻辑关系&#xff0c;抽象概括 五、自上而下表达&#xff0c;结论先行 第1章 为什么要用金字塔结构 如果受众希望通过阅读你的文章、听…

C++学习贴---C++预处理器

文章目录 前言预处理器#define预处理条件编译#ifdef#ifndef#if、#elif、#else 和 #endif #和##运算符 预定义宏 前言 预处理器 预处理器是指一些指示编译器在实际编译之前所需要完成的指令。 预处理器负责处理以**井号&#xff08;#&#xff09;**开头的预处理指令&#xff0…

lv11 嵌入式开发 ARM体系结构理论基础(异常、微架构)4

1 异常概念 处理器在正常执行程序的过程中可能会遇到一些不正常的事件发生 这时处理器就要将当前的程序暂停下来转而去处理这个异常的事件 异常事件处理完成之后再返回到被异常打断的点继续执行程序 2 异常处理机制 不同的处理器对异常的处理的流程大体相似&#xff0c…

BMVC 23丨多模态CLIP:用于3D场景问答任务的对比视觉语言预训练

来源&#xff1a;投稿 作者&#xff1a;橡皮 编辑&#xff1a;学姐 论文链接&#xff1a;https://arxiv.org/abs/2306.02329 摘要&#xff1a; 训练模型将常识性语言知识和视觉概念从 2D 图像应用到 3D 场景理解是研究人员最近才开始探索的一个有前景的方向。然而&#xff0c…

地区 IP 库

地区 & IP 库 yudao-spring-boot-starter-biz-ip (opens new window)业务组件&#xff0c;提供地区 & IP 库的封装。 #1. 地区 AreaUtils (opens new window)是地区工具类&#xff0c;可以查询中国的省、市、区县&#xff0c;也可以查询国外的国家。 它的数据来自 …

kubectl声明式资源管理命令

目录 一、声明式资源管理介绍&#xff1a; 二、声明式相关命令&#xff1a; 1. 语法格式&#xff1a; 2. 查看资源配置清单&#xff1a; 3. 解释资源配置清单&#xff1a; 4. 修改资源配置清单并应用&#xff1a; 4.1 离线修改&#xff1a; 4.2 在线修改&#xff1a; 5. 删除资…

【qemu逃逸】XCTF 华为高校挑战赛决赛-pipeline

前言 虚拟机用户名: root 无密码 设备逆向与漏洞分析 程序没有去符合, 还是比较简单. 实例结构体如下: 先总体说一下流程: encode 为 base64 编码函数, decode 为 base64 解码函数. 然后 encPipe 和 decPipe 分别存放编码数据和解码数据, 分别有四个: 其中 EncPipeLine 中…

ElementUI的el-upload上传组件与表单一起提交遇到的各种问题以及解决办法(超详细,每个步骤都有详细解读)

背景: 使用ruoyi-vue进行2次开发,需要实现表单与文件上传一起提交,并且文件上传有4个,且文件校验很复杂,因此ruoyi-vue集成的上传组件FileUpload调试几天后发现真不太适用,最终选择element UI原生组件el-upload(FileUpload也是基于el-upload实现的),要实现表单与文件同…

Unity地面交互效果——4、制作地面凹陷轨迹

大家好&#xff0c;我是阿赵。   上一篇介绍了曲面细分着色器的基本用法和思路&#xff0c;这一篇在曲面细分的基础上&#xff0c;制作地面凹陷的轨迹效果。 一、思路分析 这次需要达到的效果是这样的&#xff1a; 从效果上看&#xff0c;这个凹陷在地面下的轨迹&#xff0…

PostMan授权认证使用

Authorization 对于很多应用&#xff0c;出于安全考虑我们的接口并不希望对外公开。这个时候就需要使用授权(Authorization)机制。 授权过程验证您是否具有访问服务器所需数据的权限。 当发送请求时&#xff0c;通常必须包含参数&#xff0c;以确保请求具有访问和返回所需数据…