算法学习系列(三十二):背包问题

目录

  • 引言
  • 一、01背包
    • 1.二维代码模板
    • 2.一维代码模板
  • 二、完全背包
    • 1.朴素代码模板
    • 2.二维优化代码模板
    • 3.一维代码模板
  • 三、多重背包
    • 1.朴素做法
    • 2.优化版本
  • 四、分组背包
    • 1.朴素做法
    • 2.一维优化

引言

从这一篇文章开始,就开始学习动态规划了,也就是DP了,然后就是DP可以说是整个算法中的最难学的部分之一,好写是非常的好写的,每道题也只有很短的代码量,但是主要是它这个动归方程不好想,也不好推导出来,而且这类题都没有一个好的模板,只能说都是通过大量的做题凭经验得出来的,所以说得好好做题,好好思考,话不多说,那就开始吧。


一、01背包

问题:每件物品有各自的重量和价值,从n件物品中任选多个,重量不能超过m,每件物品只能选一次,求能选出物品的最大价值

1.二维代码模板

思想: i i i件物品,要么拿要么不拿,如果不拿那就是 f [ i − 1 ] [ j ] f[i-1][j] f[i1][j],如果拿,那么最大值为 f [ i ] [ j ] = f [ i − 1 ] [ j − v [ i ] ] + w [ i ] f[i][j] = f[i-1][j-v[i]] + w[i] f[i][j]=f[i1][jv[i]]+w[i],也就是先找把第i个物品去除的最大值,再加上这个物品就是拿的最大值。

const int N = 1010;int n, m;
int v[N], w[N];  // 第i件物品的体积,价值(权重)
int f[N][N];int main()
{scanf("%d%d", &n, &m);for(int i = 1; i <= n; ++i) scanf("%d%d", &v[i], &w[i]);for(int i = 1; i <= n; ++i){for(int j = 1; j <= m; ++j){f[i][j] = f[i-1][j];if(j >= v[i]) f[i][j] = max(f[i][j], f[i-1][j-v[i]] + w[i]);}}printf("%d\n", f[n][m]);return 0;
}

2.一维代码模板

优化对代码或方程进行等价变形

思路:首先 f [ i ] [ j ] f[i][j] f[i][j] 中用的都是 i − 1 i-1 i1,而没有用到 i − 2 , i − 3... i-2,i-3... i2,i3...,所以就可以用滚动数组来,就可以去掉一维。然后对方程进行等价变形,之后由于 j j j是从 v [ i ] v[i] v[i] m m m,由小到大变的,所以再计算 f [ j − v [ i ] ] f[j-v[i]] f[jv[i]]时,由于 j − v [ i ] < = j j-v[i] <= j jv[i]<=j,所以 f [ j − v [ i ] ] f[j-v[i]] f[jv[i]]计算的是第 i i i层的已经被算过了,而应该算的是 i − 1 i-1 i1层的,所以 j j j改为由大到小遍历,这样 f [ j − v [ i ] ] f[j-v[i]] f[jv[i]]在遍历时就是第i-1层的了,这样就对了

const int N = 1010;int n, m;
int v[N], w[N];  
int f[N];int main()
{scanf("%d%d", &n, &m);for(int i = 1; i <= n; ++i) scanf("%d%d", &v[i], &w[i]);for(int i = 1; i <= n; ++i){for(int j = m; j >= v[i]; --j){f[j] = max(f[j], f[j-v[i]] + w[i]);}}printf("%d\n", f[m]);return 0;
}

二、完全背包

问题:每个物品可以选无数个,其余跟01背包是一样的

1.朴素代码模板

const int N = 1010;int n, m;
int v[N], w[N];
int f[N][N];int main()
{cin >> n >> m;for(int i = 1; i <= n; ++i) cin >> v[i] >> w[i];for(int i = 1; i <= n; ++i){for(int j = 1; j <= m; ++j){for(int k = 0; k * v[i] <= j; ++k){f[i][j] = max(f[i][j], f[i-1][j-k*v[i]] + k*w[i]);}}}cout << f[n][m] << endl;return 0;
}

2.二维优化代码模板

核心:在k次循环里 f [ i ] [ j ] = f [ i ] [ j − v [ i ] ] + w [ i ] f[i][j] = f[i][j-v[i]] + w[i] f[i][j]=f[i][jv[i]]+w[i]

const int N = 1010;int n, m;
int v[N], w[N];
int f[N][N];int main()
{cin >> n >> m;for(int i = 1; i <= n; ++i) cin >> v[i] >> w[i];for(int i = 1; i <= n; ++i){for(int j = 1; j <= m; ++j){f[i][j] = f[i-1][j];if(j >= v[i]) f[i][j] = max(f[i][j], f[i][j-v[i]] + w[i]);}}cout << f[n][m] << endl;return 0;
}

3.一维代码模板

思路:降维的过程,跟01背包是一样的,然后由于这个所优化后的 f [ j − v [ i ] ] f[j-v[i]] f[jv[i]]就是第i层的,所以 j j j从小到大遍历就是正确的。
然后这个代码发现跟01背包基本是一样的,只不过 j j j是01背包是从大到小,完全背包是从小到大。

const int N = 1010;int n, m;
int v[N], w[N];
int f[N];int main()
{cin >> n >> m;for(int i = 1; i <= n; ++i) cin >> v[i] >> w[i];for(int i = 1; i <= n; ++i){for(int j = v[i]; j <= m; ++j){f[j] = max(f[j], f[j-v[i]] + w[i]);}}cout << f[m] << endl;return 0;
}

三、多重背包

问题:每件物品有 s [ i ] s[i] s[i]个,其余跟完全背包一样

1.朴素做法

思想:跟完全背包一样,只不过k限制个数

const int N = 110;int n, m;
int v[N], w[N], s[N];
int f[N][N];int main()
{cin >> n >> m;for(int i = 1; i <= n; ++i) cin >> v[i] >> w[i] >> s[i];for(int i = 1; i <= n; ++i){for(int j = 1; j <= m; ++j){for(int k = 0; v[i] * k <= j && k <= s[i]; ++k){f[i][j] = max(f[i][j], f[i-1][j-k*v[i]] + k*w[i]);  //一个都不选,选k个}}}cout << f[n][m] << endl;return 0;
}

2.优化版本

思想:就是把这个背包变成01背包,第 i i i件物品选 k k k件,把这 k k k件物品当成一个物品,k以 2 2 2次幂增长,最后01背包选的时候可以通过选多个物品可以凑出 2 k 2^k 2k种可能的结果。然后每个物品依次这样,体积和权重就是单个乘以选的总数,最后再用01背包解决就行了,相当于把所有种组合罗列出来,算最优解。

const int N = 25000, M = 2010;int n, m;
int v[N], w[N];
int f[N];int main()
{cin >> n >> m;int cnt = 0;for(int i = 1; i <= n; ++i) {int a, b, s;cin >> a >> b >> s;int k = 1;while(k <= s){cnt++;v[cnt] = a * k;w[cnt] = b * k;s -= k;k <<= 1;  //每件物品背包里会有k^2个,然后可以凑出任意一种情况}if(s > 0){cnt++;v[cnt] = a * s;w[cnt] = b * s;}}n = cnt;for(int i = 1; i <= n; ++i){for(int j = m; j >= v[i]; --j){f[j] = max(f[j], f[j-v[i]] + w[i]);}}cout << f[m] << endl;return 0;
}

四、分组背包

问题:有多个背包,每个背包中最多只能选一件物品,在规定的体积中,选出最大价值

1.朴素做法

思想:其实就是多个01背包,每个背包中要么不选,要么选一个

#include <cstdio>
#include <iostream>using namespace std;const int N = 110;int n, m;
int v[N][N], w[N][N], s[N];
int f[N][N];int main()
{cin >> n >> m;for(int i = 1; i <= n; ++i){cin >> s[i];for(int j = 1; j <= s[i]; ++j) cin >> v[i][j] >> w[i][j];}for(int i = 1; i <= n; ++i){for(int j = 1; j <= m; ++j){f[i][j] = f[i-1][j];  //不选for(int k = 1; k <= s[i]; ++k){if(v[i][k] <= j) f[i][j] = max(f[i][j], f[i-1][j-v[i][k]] + w[i][k]);  //选第k个}}}cout << f[n][m] << endl;return 0;
}

2.一维优化

思想:跟之前讲的优化一样

#include <cstdio>
#include <iostream>using namespace std;const int N = 110;int n, m;
int v[N][N], w[N][N], s[N];
int f[N];int main()
{cin >> n >> m;for(int i = 1; i <= n; ++i){cin >> s[i];for(int j = 1; j <= s[i]; ++j) cin >> v[i][j] >> w[i][j];}for(int i = 1; i <= n; ++i){for(int j = m; j > 0; --j){for(int k = 1; k <= s[i]; ++k){if(v[i][k] <= j) f[j] = max(f[j], f[j-v[i][k]] + w[i][k]);}}}cout << f[m] << endl;return 0;
}

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

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

相关文章

js手写Promise(下)

目录 resolve与reject的调用时机封装优化 回调返回PromiseisPromise手动调用then 微队列catchresolverejectall传入的序列为空传入的值非Promise race完整的Promise代码 如果没有看过上半部分的铁铁可以看看这篇文章 js手写Promise&#xff08;上&#xff09; resolve与reject…

MacOS 设置 环境变量

1. 以TextEdit 的方式打开 ~/.bash_profile 文件 touch ~/.bash_profile; open -t ~/.bash_profile 2.新增环境变量 export PATH"$HOME/.rbenv/bin:$PATH" 3.让以上所作的配置生效 source ~/.bash_profile 4.查看是否生效&#xff08;有时可能须要关闭当前 T…

Go Context -- 管理请求的上下文信息

在Go语言中&#xff0c;管理请求的上下文信息对于构建可靠的并发程序至关重要。context 包为我们提供了一种优雅的方式来传递请求的取消信号、超时信息和请求范围的值。接下来将深入探讨Go中的 context 包&#xff0c;包括其基本概念、用法、实际应用场景和最佳实践&#xff0c…

在Visual Studio中引用和链接OpenSceneGraph (OSG) 库

在Visual Studio中引用和链接OpenSceneGraph (OSG) 库&#xff0c;按照以下步骤操作&#xff1a; 构建或安装OSG库 下载OpenSceneGraph源代码&#xff08;如3.0版本&#xff09;并解压。使用CMake配置项目&#xff0c;为Visual Studio生成解决方案文件。通常您需要设置CMake中的…

Office2013下载安装教程,保姆级教程,附安装包和工具

前言 Microsoft Office是由Microsoft(微软)公司开发的一套基于 Windows 操作系统的办公软件套装。常用组件有 Word、Excel、PowerPoint、Access、Outlook等。 准备工作 1、Win7 及以上系统 2、提前准备好 Office 2013 安装包 安装步骤 1.鼠标右击【Office2013(64bit)】压缩…

Vue中 常用的修饰符有哪些

Vue是一款建立在JavaScript框架上的开源前端库&#xff0c;已经成为当今前端开发人员最喜爱的选择之一。它的简洁语法和强大的功能使得开发者可以轻松地构建交互性的网页应用程序。在Vue中&#xff0c;修饰符是一个重要的概念&#xff0c;它们可以帮助我们更好地控制和定制DOM元…

PV、UV、IP

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言1. PV1.1 PV 计算1.2 PV 的影响因素 2. UV2.1 UV 计算2.2UV 的影响因素 3. IP3.1 IP和UV①UV大于IP②UV小于IP 三者的关系PV 和 UV 前言 PV、UV、IP是我们在运…

92.使用数组形式的责任链模式实现项目配置初始化

文章目录 前言示例 前言 Golang 中&#xff0c;可以使用接口&#xff08;interface&#xff09;来实现一种配置模式&#xff0c;其中配置对象实现一个接口&#xff0c;并提供一个Apply()方法来应用配置。这样&#xff0c;可以使用不同的配置对象来配置不同的行为&#xff0c;而…

深度学习入门笔记(九)自编码器

自编码器是一个无监督的应用&#xff0c;它使用反向传播来更新参数&#xff0c;它最终的目标是让输出等于输入。数学上的表达为&#xff0c;f(x) x&#xff0c;f 为自编码器&#xff0c;x 为输入数据。 自编码器会先将输入数据压缩到一个较低维度的特征&#xff0c;然后利用这…

Vue3.0(五):Vue-Router 4.x详解

Vue-Router详解 vue-router教程 认识前端路由 路由实际上是网络工程中的一个术语 在架构一个网络的时候&#xff0c;常用到两个很重要的设备—路由器和交换机路由器实际上就是分配ip地址&#xff0c;并且维护着ip地址与电脑mac地址的映射关系通过映射关系&#xff0c;路由器…

读懂 FastChat 大模型部署源码所需的异步编程基础

原文&#xff1a;读懂 FastChat 大模型部署源码所需的异步编程基础 - 知乎 目录 0. 前言 1. 同步与异步的区别 2. 协程 3. 事件循环 4. await 5. 组合协程 6. 使用 Semaphore 限制并发数 7. 运行阻塞任务 8. 异步迭代器 async for 9. 异步上下文管理器 async with …

解释Java中的事务管理,以及事务的隔离级别是什么?

解释Java中的事务管理&#xff0c;以及事务的隔离级别是什么&#xff1f; 在Java中&#xff0c;事务管理是一种机制&#xff0c;用于管理对数据库进行的一系列操作&#xff0c;以确保这些操作要么全部成功执行&#xff0c;要么全部失败回滚&#xff0c;保持数据的一致性和完整…

STM32 的优势与不足

STM32 的优势和不足如下&#xff1a; 优势&#xff1a; ① 性能&#xff1a;STM32 采用了 ARM Cortex-M 核&#xff0c;具有高性能、低功耗、低成本等特点&#xff0c;可以满足各种嵌入式系统应用的需求。 ② 生态系统&#xff1a;STM32 具有完善的生态系统&#xff0c;包括各种…

戴上HUAWEI WATCH GT 4,解锁龙年新玩法

春节将至&#xff0c;华为WATCH GT 4作为一款颜值和实力并存的手表&#xff0c;能为节日增添了不少趣味和便利。无论你是钟情于龙年表盘或定制属于自己的表盘&#xff0c;还是过年用来抢红包或远程操控手机拍全家福等等&#xff0c;它都能成为你的“玩伴”。接下来&#xff0c;…

宏观行业心得

OLAP的特点 电商这样的OLTP场景大家更熟悉。相比之下&#xff0c;OLAP的特点&#xff1a; 读相对多&#xff0c;1000row以上大批写入&#xff0c;不改已有数据查询时输出很多行、很少列&#xff0c;结果被过滤或聚合后能够在一台服务器的内存中单台服务器qps数百&#xff0c;…

fastapi mysql 开发restful 3

pip install mysql-connector-python pymysql 数据库链接 创建src目录&#xff0c;里面创建db.py 代码如下&#xff1a; # 导入mysql.connector模块&#xff0c;该模块提供了与MySQL数据库进行连接和交互的功能。 import mysql.connector # 定义一个函数get_db_connectio…

二分算法--模板及原理总结

二分答案 首先我们看这个图&#xff1a; 我们需要二分的答案就是这个临界点x。 什么情况下可以使用二分呢&#xff1a; 具有单调性&#xff08;单调递增&#xff0c;单调递减&#xff09;&#xff0c;二段性&#xff08;整个区间一分为二&#xff0c;一段区间满足&#xff0c;一…

为什么许多年轻人不喜欢回农村过年了?

为什么许多年轻人不喜欢回农村过年了&#xff1f; 随着时代的变迁和社会的发展&#xff0c;越来越多的年轻人选择在春节期间留在城市&#xff0c;而不是回到农村老家过年。这一现象引起了人们的关注和思考&#xff1a;为什么许多年轻人不喜欢回农村过年了&#xff1f; 首先&a…

全栈笔记_插件篇(用Volar替换Vuter)

Volar与Vuter的区别 TS支持&#xff1a;Volar和Vuter是2个独立的插件&#xff0c;都是为.vue单文件组件提供代码高亮以及语法支持&#xff0c;但是Vuter对ts的支持并不友好。唯一根标签&#xff1a;Volar 不限制是否唯一根标签&#xff0c;vuter 则会报错 The template root r…

史上最“昂贵”的漏洞

阿丽亚娜 5 号”事故 欧洲航天局“阿丽亚娜 5 号”运载火箭在 1996 年 6 月 4 日首次发射时发生了事故。火箭在飞行的第 40 秒由于软件错误而解体并爆炸&#xff0c;该软件直接沿用了以前“阿丽亚娜 4 号”火箭的软件&#xff0c;且未在新环境中进行测试。 此次事故导致四颗卫…