【C++算法模板】背包九讲(上):01背包、完全背包、多重背包

文章目录

    • 1)01背包
      • 1:二维数组
      • 2:一维数组
    • 2)完全背包
      • 1:朴素做法
      • 2:公式优化
      • 3:再优化一维数组
    • 3)多重背包
      • 1:朴素做法
      • 2:二进制优化
      • 3:单调队列优化

1)01背包

1:二维数组

时间复杂度: O ( n 2 ) O(n^2) O(n2),空间复杂度: O ( n 2 ) O(n^2) O(n2)

  • 非常熟悉和基础,没什么可讲的
#include<bits/stdc++.h>
#define x first
#define y secondusing namespace std;typedef long long ll;
typedef pair<int,int> PII;// 解题思路: const int N=1e3+5;
const int M=1e3+5;int n,m;
int v[N],w[N];
int f[N][M];int main() {cin>>n>>m;for(int i=1;i<=n;i++) {scanf("%d%d",&v[i],&w[i]); // v:重量,w:价值}// 枚举物品for(int i=1;i<=n;i++) {// 枚举背包容量for(int j=0;j<=m;j++) {f[i][j]=f[i-1][j];// 如果能拿物品iif(j>=v[i]) f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]); // v:体积,w:价值}	}int res=0;// 当取n个物品时最大总价值,其实也就是f[n][m]for(int i=0;i<=m;i++) res=max(res,f[n][i]);cout<<res<<endl;cout<<f[n][m]<<endl;return 0;
}

2:一维数组

时间复杂度: O ( n 2 ) O(n^2) O(n2),空间复杂度: O ( n ) O(n) O(n)

  • 因为 f [ i ] [ j ] f[i][j] f[i][j] 只与 f [ i − 1 ] [ j ] f[i-1][j] f[i1][j] f [ i − 1 ] [ j − v [ i ] ] + w [ i ] f[i-1][j-v[i]]+w[i] f[i1][jv[i]]+w[i] 有关,即只与上一个状态(上一次选择)有关,那么我们只需要开一个一维数组记录上一次选择即可,这个数组名为滚动数组
  • 由于滚动数组已经表示了上一次选择的状态,所以代码 f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j]=f[i-1][j] f[i][j]=f[i1][j] 可以去掉
  • 枚举背包体积时必须从大到小,原因已经在代码中解释,如果还是 f o r ( i n t j = 0 ; j < = m ; j + + ) for(int\ j=0;j<=m;j++) for(int j=0;j<=m;j++) ,那么用到的实际上还是 f [ i ] f[i] f[i] 这个状态而不是 f [ i − 1 ] f[i-1] f[i1] 这个状态
  • 如果把所有的 f [ i ] f[i] f[i] 都初始化为 0 0 0,那么 f [ m ] f[m] f[m] 表示的是当背包体积 < = m <=m <=m 时的最大价值是多少;如果只把 f [ 0 ] f[0] f[0] 初始化为 0 0 0 ,而其他初始化为 − I N F -INF INF,那么 f [ m ] f[m] f[m] 表示的是体积刚好等于 m m m 时的最大价值,所以全部初始化为 0 0 0 的话 f [ m ] f[m] f[m] 就是答案
#include<bits/stdc++.h>
#define x first
#define y secondusing namespace std;typedef long long ll;
typedef pair<int,int> PII;// 解题思路: const int N=1e3+5; // 最大物品数
const int M=1e3+5; // 最大容量
int n,m;
int f[M]; // f[i]:物品容量为i时的背包最大容量
int v[N],w[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++) {// 因为f[i][j]只与f[i-1][j]/f[i-1][j-v[i]]+w[i],只与上一个状态有关// 所以二维数组中有很多空间被浪费,计算完就可以丢掉了// 所以用滚动数组,但是在枚举背包体积的时候必须从大到小排序,若顺序的话再用到上一个状态时就已经被更新过了for(int j=m;j>=v[i];j--) f[j]=max(f[j],f[j-v[i]]+w[i]);}int res=0;for(int i=0;i<=m;i++) res=max(res,f[i]);cout<<res<<endl;cout<<f[m]<<endl;return 0;
}

2)完全背包

1:朴素做法

时间复杂度: O ( n 3 ) O(n^3) O(n3),空间复杂度: O ( n 2 ) O(n^2) O(n2)

  • 时间复杂度和空间复杂度都较高,手动枚举每个物品可选择数量
#include<bits/stdc++.h>
#define x first
#define y secondusing namespace std;typedef long long ll;
typedef pair<int,int> PII;// 解题思路: const int N=1e3+5;
const int M=1e3+5;
int f[N][M];
int n,m;
int v[N],w[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-v[i]*k]+w[i]*k);}}	}cout<<f[n][m]<<endl;return 0;
}

2:公式优化

时间复杂度: O ( n 2 ) O(n^2) O(n2),空间复杂度: O ( n ) O(n) O(n)

  • f [ i , j ] = m a x ( f [ i − 1 , j ] , f [ i − 1 , j − v ] + w , f [ i − 1 , j − 2 v ] + 2 w , f [ i − 1 , j − 3 v ] + 3 v , . . . ) f[i,j]=max(f[i-1,j],f[i-1,j-v]+w,\ f[i-1,j-2v]+2w,\ f[i-1,j-3v]+3v,\ ...) f[i,j]=max(f[i1,j],f[i1,jv]+w, f[i1,j2v]+2w, f[i1,j3v]+3v, ...),① f [ i , j − v ] = m a x ( f [ i − 1 , j − v ] , f [ i − 1 , j − 2 v ] + w , f [ i − 1 , j − 3 v ] + 2 w , f [ i − 1 , j − 4 v ] + 3 w , . . . ) f[i,j-v]=max(f[i-1,j-v],f[i-1,j-2v]+w,\ f[i-1,j-3v]+2w,\ f[i-1,j-4v]+3w,\ ...) f[i,jv]=max(f[i1,jv],f[i1,j2v]+w, f[i1,j3v]+2w, f[i1,j4v]+3w, ...)②,
  • 可以看出②式是①式第二项起 + w +w +w 的结果,所以 f [ i , j ] = m a x ( f [ i − 1 , j ] , f [ i , j − v ] + w ) f[i,j]=max(f[i-1,j],\ f[i,j-v]+w) f[i,j]=max(f[i1,j], f[i,jv]+w),即转移时与上一个状态无关,所以我们就可以顺序枚举物品容量
#include<bits/stdc++.h>
#define x first
#define y secondusing namespace std;typedef long long ll;
typedef pair<int,int> PII;// 解题思路: const int N=1e3+5;
const int M=1e3+5;
int n,m;
int v[N],w[N];
int f[N][M];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++) {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]); // 和01背包的区别就在于可以与当前状态有关}	}cout<<f[n][m]<<endl;return 0;
}

3:再优化一维数组

时间复杂度: O ( n 2 ) O(n^2) O(n2),空间复杂度: O ( n ) O(n) O(n)

  • 注意注释中是如何分析当 f f f 数组是一维时状态转移的是当前状态还是上一次的状态
  • 因为滚动数组本身代表上一次的状态,所以 f [ i , j ] = f [ i − 1 , j ] f[i,j]=f[i-1,j] f[i,j]=f[i1,j] 直接优化成 f [ j ] = f [ j ] f[j]=f[j] f[j]=f[j],无意义,所以不用写
#include<bits/stdc++.h>
#define x first
#define y secondusing namespace std;typedef long long ll;
typedef pair<int,int> PII;// 解题思路: const int N=1e3+5;
const int M=1e3+5;
int n,m;
int v[N],w[N];
int f[M];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++) {// f[i][j]=f[i-1][j]直接删掉一维,即f[j]=f[j](一维滚动数组直接代表上一次的状态,所以不需要这句代码)// j是从小到大枚举的for(int j=v[i];j<=m;j++) {// j-v[i]是<j的,所以算f[j]的时候f[j-v[i]]已经被算过了,所以是第i层的f[i][j-v[i]]f[j]=max(f[j],f[j-v[i]]+w[i]);}	}cout<<f[m]<<endl;return 0;
}

3)多重背包

1:朴素做法

时间复杂度: O ( n 3 ) O(n^3) O(n3),空间复杂度: O ( n 2 ) O(n^2) O(n2)

  • 类似于完全背包的朴素做法,直接第三层去枚举物品的个数,不过多了一个限制条件, k < = s [ i ] k<=s[i] k<=s[i]
#include<bits/stdc++.h>
#define x first
#define y secondusing namespace std;typedef long long ll;
typedef pair<int,int> PII;// 解题思路: const int N=1e2+5;
const int M=1e2+5;
int n,m;
int v[N],w[N],s[N];
int f[N][M];int main() {cin>>n>>m;for(int i=1;i<=n;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++) {// 能拿的个数从0~s[i],且k*v[i]能被背包装下for(int k=0;k<=s[i] && 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:二进制优化

时间复杂度: O ( n 2 l o g n ) O(n^2log^n) O(n2logn),空间复杂度: O ( n ) O(n) O(n)

  • 如果按照类似于完全背包从状态转移方程式入手的角度来优化是不可行的,因为完全背包问题不会多最后一项出来,多了一个 i f if if 的判定条件,最后一项会因为超出了 j j j 而被舍去

  • 如何优化呢?若 s = 1023 s=1023 s=1023,那么 1023 1023 1023 能由其 s u m ( 2 n ) < = 1023 sum(2^n)<=1023 sum(2n)<=1023 的这 n n n 2 2 2 的指数组成( n = 9 n=9 n=9),把 1023 1023 1023 个物品当作是 1023 1023 1023 01 01 01 背包,再通过二进制优化的方式把从枚举 1023 1023 1023 次转换为只需要枚举 9 9 9 次(倍增法思想)

  • 比如 s = 200 s=200 s=200 ∑ i = 1 n 2 i < = 200 \sum_{i=1}^n\ 2^i<=200 i=1n 2i<=200 可得 { 1 , 2 , 4 , 8 , 16 , 32 , 64 } \{1,\ 2,\ 4,\ 8,\ 16,\ 32,\ 64\} {1, 2, 4, 8, 16, 32, 64},这几个数字之和为 127 127 127,则只需要再来一个 73 73 73 就可以凑出 [ 0 , 200 ] [0,200] [0,200] 的数字;即对于任意一个 s s s ,我们可以凑成 { 1 , 2 , 4 , 8 , . . . , 2 k , c } \{1,\ 2,\ 4,\ 8,\ ...,\ 2^k,\ c\} {1, 2, 4, 8, ..., 2k, c},其中 c < 2 k + 1 c<2^{k+1} c<2k+1

#include<bits/stdc++.h>
#define x first
#define y secondusing namespace std;typedef long long ll;
typedef pair<int,int> PII;// 解题思路: const int N=2e4+5; // 1000*log(2000),2w4左右
const int M=2e3+5;
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;scanf("%d%d%d",&a,&b,&s);int k=1; // 从2^0开始,每组初始物品数while(k<=s) {// 每次把k个第i个物品打包在一起cnt++; // 编号++v[cnt]=a*k;w[cnt]=b*k;s-=k;k*=2;}// 补常数if(s>0) {cnt++; // 第i个物品的最后一次分组v[cnt]=a*s;w[cnt]=b*s;}}n=cnt; // 物品个数变成总的打包数for(int i=1;i<=n;i++) {// 至少能拿一个物品,所以j>=v[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;
}

3:单调队列优化

  • 看不懂,溜了溜了

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

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

相关文章

AcWing-直方图中最大的矩形

131. 直方图中最大的矩形 - AcWing题库 所需知识&#xff1a;单调栈 思路&#xff1a;要求最大矩形&#xff0c;所以需要使矩形的高与长的乘积最大即可&#xff0c;依次从左到右将每一列当作中心列&#xff0c;向两边扩散&#xff0c;直到两边的高都小于该列的高&#xff0c;…

Vmware虚拟机Centos7固定IP地址

1、点击编辑-虚拟网络编辑器 2、点击更改设置、修改虚拟网络配置器并确认保存&#xff08;见图&#xff09; 这个子网IP和子网掩码的前三位需要一样网关的前三位需要和子网ip一致。 3、打开设置“网络和Internet”&#xff0c;点击“更改适配器选项”&#xff0c;点击适配器VM…

PP-LCNet:一种轻量级CPU卷积神经网络

PP-LCNet: A Lightweight CPU Convolutional Neural Network 最近看了一个新的分享&#xff0c;在图像分类的任务上表现良好&#xff0c;具有很高的实践意义。 论文&#xff1a; https://arxiv.org/pdf/2109.15099.pdf项目&#xff1a; https://github.com/PaddlePaddle/Padd…

JUC并发编程2(高并发,AQS)

JUC AQS核心 当有线程想获取锁时&#xff0c;其中一个线程使用CAS的将state变为1&#xff0c;将加锁线程设为自己。当其他线程来竞争锁时会&#xff0c;判断state是不是0&#xff0c;不是自己就把自己放入阻塞队列种&#xff08;这个阻塞队列是用双向链表实现&#xff09;&am…

探索ChatGPT-Plus:AI 助手全套开源解决方案

探索ChatGPT-Plus&#xff1a;AI 助手全套开源解决方案 ChatGPT-plus是一种新型的对话生成模型&#xff0c;它是在OpenAI的ChatGPT基础上进行了改进和优化的版本。ChatGPT-plus的出现引起了广泛关注&#xff0c;因为它在对话生成方面展现出了更加出色的表现和能力。在本文中&am…

蒙特卡洛方法【强化学习】

强化学习笔记 主要基于b站西湖大学赵世钰老师的【强化学习的数学原理】课程&#xff0c;个人觉得赵老师的课件深入浅出&#xff0c;很适合入门. 第一章 强化学习基本概念 第二章 贝尔曼方程 第三章 贝尔曼最优方程 第四章 值迭代和策略迭代 第五章 强化学习实践—GridWorld 第…

flutter嵌入原生view

一、iOS端(Swift实现) 1. 新建原生view(NativeView.swift) &#x1f3f7;️ 需要继承FlutterPlatformView &#xff0c;实现view()方法 import Foundation import Flutterclass NativeView: NSObject, FlutterPlatformView {private var _view: UIViewinit(frame: CGRect,view…

从文件夹(包含子文件夹)找到的包含特定关键词的 Word 文档复制到一个新的文件夹中

import os import glob import shutildef find_and_copy_docs(root_dir, keyword, target_dir):"""在指定的目录及其子目录下查找包含特定关键词的 Word 文档&#xff0c;并将它们复制到目标文件夹。参数:root_dir: 要搜索的根目录路径。keyword: 需要匹配的关键…

make/makefile学习

文章目录 1、makefile函数1.1、字符串替换函数&#xff1a;subst1.2、模式字符串替换函数&#xff1a;patsubst1.3、去空格函数&#xff1a;strip1.4、查找字符串函数&#xff1a;findstring 2、、:、&#xff1f;区别 1、makefile函数 1.1、字符串替换函数&#xff1a;subst …

TS中,PropType导入报错is a type and must be imported using a type-only import...

报错信息&#xff1a; PropType is a type and must be imported using a type-only import when verbatimModuleSyntax is enabled.ts(1484) (alias) type PropType<T> PropConstructor<T> | PropConstructor<T>[] import PropType问题分析&#xff1a; …

《QT实用小工具·二十》存款/贷款计算器

1、概述 源码放在文章末尾 该项目实现了用于存款和贷款的计算器的功能&#xff0c;如下图所示&#xff1a; 项目部分代码如下&#xff1a; #ifndef WIDGET_H #define WIDGET_H#include <QWidget>namespace Ui { class Widget; }class Widget : public QWidget {Q_OBJ…

构造函数,原型对象,对象实例 以及原型链的关系

当我们使用构造函数new的方式创建实例对象&#xff0c;此时构造函数的.prototype属性就是实例对象的原型对象&#xff0c;实例对象可以通过.__proto__来访问到原型对象&#xff0c;同时实例对象会继承原型对象的属性方法。 构造函数和原型对象的关系其实是被包含的关系&#x…

【电子通识】普通电阻、敏感电阻、可调电阻的种类和特点

电阻的作用 在【分立元件】理解电阻 中我们知道电阻是在电路中对电流产生阻碍作用的元件。电阻是电子产品中最基本、最常用的电子元件之一。 有各产品的电路板中基本都有电阻器&#xff0c;通常起限流、滤波或分压等作用。实际上&#xff0c;电阻器的种类很多&#xff0c;根据其…

中服云数字孪生平台 3.0 版全新升级!不仅更好看,而且更好用!

近日&#xff0c;中服云数字孪生平台迎来版本升级。中服云数字孪生平台 3.0 版&#xff0c;以物联网平台 数据中台为基础&#xff0c;以 2D/3D 为展示形式&#xff0c;旨在打造一个从设备数据到孪生应用的一站式数智化平台。 中服云数字孪生平台架构 新版升级 功能急速迭代 …

vue-router(进阶版)

前言 相信大家也是从我的路由初级篇那边过来的,没看的话也可以先从初级开始看,可以提高后续的理解能力 路由初级篇的链接 别的也就不多说了,直接开始 路由的主体构成 前言 之前的路由写法虽然可以使用但是会导致main.js过于臃肿,所以后续基本不会再这么使用了, 初始化路由 创…

4.Hexo 页面属性和模板设置

Frontmatter frontmatter基本上是可以定义的有关不同文件的信息&#xff0c;本质上是元数据 frontmatter是我们可以分配给每个内容页面的信息 在Hexo中创建文件时&#xff0c;Hexo主题可以使用该信息以不同的方式显示该内容 当在Hexo创建了一个文件&#xff0c;在source文件夹…

【JavaWeb】Tomcat服务器

目录 动态网站动态网站的特点 程序架构B/S与C/S的比较B/S技术的工作原理URL 什么是Web服务器 Web服务器、服务端、服务器的区别和联系什么是TomcatTomcat服务器的安装与配置解压缩版本Tomcat的配置添加系统变量&#xff0c;名称为CATALINA_HOME&#xff0c;值为Tomcat的安装目录…

Go-学会string的基本使用

本节重点&#xff1a; 学会 sting 的基本使用 字符串在 Go 中值得特别提及&#xff0c;因为与其他语言相比&#xff0c;string 的实现方式同其他语言略有不同。 什么是字符串&#xff1f; 字符串是 Go 中的字节切片。可以通过将一组字符括在双引号中来创建字符串" "…

OpenTelemetry——What is OpenTelemetry

What is OpenTelemetry? 什么是 OpenTelemetry&#xff1f; A short explanation of what OpenTelemetry is and isn’t. 对 OpenTelemetry 是什么和不是什么的简短说明 OpenTelemetry is an Observability framework and toolkit designed to create and manage telemetry d…

Qt中播放GIF动画

在Qt应用程序中&#xff0c;如果你想在QLabel控件上播放GIF动画&#xff0c;可以使用QMovie类与QLabel配合来实现。以下是详细步骤和代码示例&#xff1a; 步骤1&#xff1a;引入必要的头文件 首先&#xff0c;在你的源代码文件中包含QMovie和QLabel相关的头文件&#xff1a;…