背包问题学习

背包问题是常见的动态规划dp的问题

下面用到的符号:

  • 常用n表示物品数, m表示背包容积
  • f[i][j]表示i件物品, j的背包容量的最大价值
  • w[i]表示第i件物品的价值, v[i] 表示第i件物品的容量
  • f[0][0~m] = 0, 所以n可以从1开始遍历
  • 一般是有两层嵌套循环 第一层遍历物品, 第二层遍历背包容量, 第三层视情况, 若完全背包or多重背包 需要遍历决策 判断 k*v[i] <= j (背包容量)

0/1背包

下图是0/1背包的分析:
0/1背包问题的分析

题目 (0/1背包)

在这里插入图片描述

输入输出

输入输出

核心代码

for(int i = 1; i <= n; i++)
    for(int j = 0; j <= m; j++){ // 不难发现 f[i][] 都是依赖于 f[i-1][] 的
        f[i][j] = f[i-1][j];
    	if(v[i]<=j) f[i][j] = max(f[i][j], f[i-1][j-v[i]]+w[i]);    
}

优化: 降维 二维 -> 一维

需要注意的是在遍历背包容量的时候, 需要逆序遍历背包容量, 因为物品只能取一个和不取两种状态, 而逆序能够保证这个。

完整代码如下:

#include<iostream>
#include<algorithm>

using namespace std;
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;
}

完全背包

下图是完全背包的分析:

完全背包分析

题目(完全背包 物品可以无限拿, 只要背包容量还能放得下)
题目和输入/输出
题目输入和输出

朴素做法(三层循环)

#include<iostream>
#include<algorithm>

using namespace std;

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++) scanf("%d%d",&v[i],&w[i]);
    
    for(int i = 1; i<=n; i++){ // 列举物品体积
        for(int j = 0; 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]]+w[i]*k);
        }
    }
    cout<<f[n][m]<<endl;
    return 0;
}

优化:

(通过列举 f[i][j] 以及 f[i][j-v]两种情况, 发现有很多相同之处, 只相差个v) => 化简得

f[i]][j] = max(f[i-1][j], f[i-1][j-v]+w) 两种状态, 一个是取第i件物品, 另一个是不取第i件物品

 for(int i = 1; i <= n; i++){ // 列举物品
     for(int j = 0; 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]);
     }
 }

其实上述代码与0/1背包的代码有很多雷同的地方(都是两种状态, 要么取第i件, 要么不取), 只不过 0/1 背包是 f[i][j] = max(f[i][j], f[i-1][j-v[i]]+w[i]), 而完全背包是 f[i][j] = max(f[i][j], f[i][j-v[i]]+w[i]) (前一部分是不取第i件, 后面是取第i件的方案)
这个状态转移方程的含义可以这样理解:

  1. 不取第 i 件物品:这种情况下,背包的最大价值保持不变,即为 f[i][j]。这实际上是上一个状态的值,因为我们没有添加新的物品。
  2. 取第 i 件物品:如果我们决定把第 i 件物品放入背包中,那么背包的剩余容量变为 j-v[i]。因此,当前的总价值是放入这件物品之前的总价值 f[i][j-v[i]] 加上这件物品的价值 w[i]

对于上面代码的转化:

for(int i = 1; i<=n; i++){
    for(int j = v[i]; j <= m; j++){
        f[i][j] = max(f[i-1][j], f[i][j-v[i]]+w[i]);
    }
}

继续优化:

2维压缩成1维: 这里因为完全背包问题, 物品可以取无数次, 所以第二层循环循环遍历背包容量时, 顺序遍历

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;

多重背包

与上面的背包问题不同的是每个物品数量有上限: 0~s[i]
题目:
题目输入输出

状态划分: 两种 -> 取第i件物品 / 不取第i件物品

朴素做法

状态方程:f[i][j] = max(f[i-1][j-k*v[i]]+w[i]*k, f[i][j]), k = 0, 1, 2... s[i]

初级版 多重背包

 #include<iostream>
 #include<algorithm>
 using namespace std;

 const int N = 110;
 int n, m;
 int s[N], v[N], w[N];
 int f[N][N]; // 1~N 个物品, 背包容量为N 所能达到的最大价值 (max)
 int main(){
     scanf("%d%d", &n,&m);
     for(int i = 1; i<=n; i++){ // 输入第i个物品的体积、价值和重量
         scanf("%d%d%d",&v[i],&w[i],&s[i]);
     }
     for(int i = 1; i <= n; i++){ // 物品
         for(int j = 0; j <= m; j++){ // 背包体积
             for(int k = 0; k <= s[i] && k*v[i] <= j; k++)
                 f[i][j] = max(f[i-1][j], f[i][j-k*v[i]]+w[i]*k);
         }
     }
     printf("%d\n",f[n][m]);
     return 0;
 }

当数据量比较大的时候, 上面的代码会出现 TLE, 因此我们需要对上述代码进行优化
优化:

困难版 多重背包
我们可以尝试展开f, 尝试寻找规律

f[i][j] = max(f[i-1][j], f[i-1][j-v]+w,...,f[i-1][j-sv]+sw)

f[i][j-v] = max( f[i-1][j-v], f[i-1][j-2v]+w,...+f[i-1][j-sv]+(s-1)w, f[i-1][j-s+1]v+sw)

但是从上式找不到其中规律,所以下述使用二进制拆解第i个物品个数s[i] 去优化上述代码

2^n的方式进行拆解包-> 01背包问题 s->logs 组, 且每组只能选一次 (拆分)

 #include<iostream>
 #include<algorithm>
 using namespace std;
 
 const int N = 25000, M = 2010; // 物品数量N NlogS 1000*log2000, 背包体积 M
 int n, m;
 int v[N], w[N]; // 物体体积, 价值
 int f[N]; // dp数组, 这里使用一维数组, 因为物品数量进行重组了
 
 /*
     多重背包的优化 (二进制对物品数量进行拆分)
     对物品进行拆分, 这样就少了一层循环(第三层k)
 */
 
 int main(){
     cin>>n>>m;
     int cnt = 0;
     for(int i = 1; i <= n; i++){
         int a, b, s;
         cin>>a>>b>>s; // 输入 第i种物品的体积, 价值
         int k = 1;
         while(k <= s){ // 拆分次数 s[i]!
             cnt++;
             v[cnt] = a * k; // k个第i件物品体积 k个 第i件物品体积为a
             w[cnt] = b * k;  // 更新第i个物品的价值
             s -= k;
             k *= 2;
         }
         if(s > 0){ // 剩余的(s个物品)为一件
             cnt++;
             v[cnt] = a*s; 
             w[cnt] = b*s;
         }
     }
 
     n = cnt; // 更新物品数量
     // 后继转为 0/1 背包问题, 每个物品 要么拿一次, 要么不拿
     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;
 }

上述介绍了常见的三种背包问题的思路和基础代码模板以及相应的优化。希望各位大佬能够给予指导以及相应的意见, 后续时间也会取更新更多更丰富的算法内容, 谢谢大家的观看♥~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计网Lesson6 - IP 地址分类管理

文章目录 1. I P IP IP 地址定义2. I P v 4 IPv4 IPv4 的表示方法2.1 I P v 4 IPv4 IPv4 的分类编址法2.2 I P v 4 IPv4 IPv4 的划分子网法2.2.1 如何划分子网2.2.2 如何确定子网的借位数2.2.3 总结2.2.4 题目练习 2.3 I P v 4 IPv4 IPv4 的无分类编址法 1. I P IP IP 地…

zabbix的自动发现机制、代理功能、SNMP监控

一、自动发现&#xff08;不安全&#xff0c;有时会失效&#xff0c;建议手动添加主机&#xff09; 1、定义 zabbix主动与服务端联系&#xff0c;将自己的地址和端口发送给服务端&#xff0c;实现自动添加监控主机 客户端是主动的一方 2、缺点 若自定义网段中主机数量太多…

电商API接口开发和接入说明{包含淘宝/京东/拼多多/抖音}

“为什么改了这个没告诉我” “实际功能和文档上说的不一样啊”。 这些话大家在进行电商API接口开发时&#xff0c;想必耳朵都听出老茧了。 真不是故意的&#xff0c;有时候任务比较急&#xff0c;就先改了代码&#xff0c;想着以后再同步文档&#xff0c;然后就给忘了。 项…

HarmonyOS带大家创建自己的第一个Page页面并实现路由跳转

我们 在开发过程中 经常会看到 被 艾特修饰的代码 有限像 java中的注解 在 harmonyOS 中 这叫 装饰器 被关键字装饰取来的代码 会具备某某功能 我们这里先来创建一个新的界面 在pages 目录下 右键 如下图 选择page创建 这里 我们取名叫 AppView 然后点击右下角 Finish 这样…

线程池原理初探

1.引言 合理利用线程池能够带来三个好处。第一&#xff1a;降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。第二&#xff1a;提高响应速度。当任务到达时&#xff0c;任务可以不需要的等到线程创建就能立即执行。第三&#xff1a;提高线程的可管理性。…

CoreDNS实战(七)-日志处理

本文主要用于介绍CoreDNS用来记录日志的几种方式以及在生产环境中遇到的一些问题和解决方案。 1 log插件 coredns的日志输出并不如nginx那么完善&#xff08;并不能在配置文件中指定输出的文件目录&#xff0c;但是可以指定日志的格式&#xff09;&#xff0c;默认情况下不论…

手写分析文件大小工具

背景&#xff1a; window 用久了磁盘变红了&#xff0c;又不想安装大文件分析的软件&#xff0c;突发奇想能否自己写一个代码&#xff0c;分析有哪些大文件 文件的单位&#xff0c;最高记作G // 文件大小单位static String[] fileSizeUnits {"B", "KB", …

SpringBoot + Spring Cloud Alibaba + Nacos实现服务管理

1、参考文档 Spring Cloud Alibaba参考文档 https://spring-cloud-alibaba-group.github.io/github-pages/hoxton/zh-cn/index.html Spring Cloud Alibaba官方文档 https://github.com/alibaba/spring-cloud-alibaba/wiki/ 2、引入 Alibaba 依赖 每个 SpringBoot 都有对应的…

kubernetes详解——从入门到入土(更新中~)

k8s简介 编排工具&#xff1a;系统层面ansible、saltstackdocker容器docker compose docker swarm docker machinedocker compose&#xff1a;实现单机容器编排docker swarm&#xff1a;实现多主机整合成为一个docker machine&#xff1a;初始化新主机mesos marathonmesos …

如何编写一份完整的软件测试报告?(进阶版)

作为测试从业者&#xff0c;编写测试用例&#xff0c;测试计划&#xff0c;测试报告都是必经之路&#xff0c;最近完成了年终述职以及版本准出&#xff0c;感觉测试报告或者各类报告真是职场人不可或缺的一项技能&#xff0c;趁着热乎劲&#x1f525;&#xff0c;写下一些注意事…

win10下maven安装与配置

1.下载安装 去官网下载最新版的安装包&#xff0c;然后解压到安装目录。 2.配置 右键桌面的计算机图标&#xff0c;属性–>高级系统设置–>环境变量&#xff0c;添加M2_HOME的环境变量&#xff0c;然后将该变量加入的PATH中。 如果想要修改maven的本地仓库位置&…

Hadoop完全分布式搭建教程(完整版)

分别创建三个节点 master slave1 slave2 在master节点下安装jdk # 解压 [rootmaster /]# tar -zxvf /opt/software/jdk-8u212-linux-x64.tar.gz -C /opt/module/ # 修改安装包名为 java [rootmaster /]# mv /opt/module/jdk1.8.0—212/ /opt/module/java# 配置环境变量并使其生…

利用 EC2 和 S3 免费搭建私人网盘

网盘是一种在线存储服务&#xff0c;提供文件存储&#xff0c;访问&#xff0c;备份&#xff0c;贡献等功能&#xff0c;是我们日常中不可或缺的一种服务。 &#x1f4bb;创建实例 控制台搜索EC2 点击启动EC2 选择AMI 选择可免费试用的 g代表采用了Graviton2芯片。 配置存储 配…

黑苹果配置清单

手里的MacBookPro已经快沦为电子垃圾了&#xff0c;平时用MacOS比较多&#xff0c;Window用的比较少&#xff0c;而苹果电脑的价格不管是MacBookPro还是MacMini丐版的便宜但是面对现在Window动不动就64g内存的情况就显得微不足道了&#xff0c;高配的价格直接把我劝退&#xff…

Ansys Speos SSS|执行 Camera Sensor模拟结果后处理

附件下载 联系工作人员获取附件 概述 本文是Speos Sensor System&#xff08;SSS&#xff09;的使用指南&#xff0c;这是一个强大的解决方案&#xff0c;用于camera sensor模拟结果的后处理。本文的目的是通过一个例子来理解如何正确使用SSS。当然本文描述的分析步骤适合任…

【数据结构和算法】找出叠涂元素

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、题目描述 二、题解 三、代码 四、复杂度分析 前言 这是力扣的2661题&#xff0c;难度为中等&#xff0c;解题方案有很多种&…

面试就是这么简单,offer拿到手软(四)—— 常见java152道基础面试题

面试就是这么简单&#xff0c;offer拿到手软&#xff08;一&#xff09;—— 常见非技术问题回答思路 面试就是这么简单&#xff0c;offer拿到手软&#xff08;二&#xff09;—— 常见65道非技术面试问题 面试就是这么简单&#xff0c;offer拿到手软&#xff08;三&#xff…

感冒 发烧 咳嗽记录

感冒 风寒: 清鼻涕 热感冒: 细菌记录, 脓鼻涕. 咳嗽 先是清痰咳嗽, 后是浓痰,细菌感染, 白细胞噬菌体, 所以要补充蛋白质,维生素. 胸骨上窝 , 天突穴 ,后面上支气管的位置, 往下会变成左右两支,连接到肺部 普通咳嗽: 用哈气拍打背部的方式. 把痰去除. 吃点 盐酸氨溴索片 增加支…

17.认识下Docker之docker的核心原理(2)

1.容器-我的小世界 不知道大家看没看过小说《完美时间》&#xff0c;里面石昊经常进入一个小世界在里面与世隔绝的修炼或者战斗&#xff0c;总之就是在一个完全封闭的空间里做他想做的事情而与外界隔离&#xff0c;不受侵扰。通过前面的分析我们知道&#xff0c;Namepace让应用…

滚动翻页效果

效果&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>Document&…