动态规划之——背包DP(进阶篇)

news2024/11/26 9:36:15

文章目录

  • 概要说明
  • 多重背包(朴素算法)
    • 模板例题
    • 思路
    • code
  • 多重背包(二进制优化)
    • 模板例题
    • 思路
    • code
  • 多重背包(队列优化)
    • 模板例题
    • 思路
  • 混合背包
    • 模板例题
    • 思路
    • code1
    • code2
  • 二维费用背包
    • 模板例题
    • 思路
    • code

概要说明

本文讲多重背包、混合背包以及二维费用背包,至于其他背包问题后续补充
01背包和完全背包问题:传送门

多重背包(朴素算法)

模板例题

点这里~~

思路

多重背包也是 0-1 背包的一个变式,与 0-1 背包的区别在于每种物品有 k 个,而非一个
那么我们可以在01背包的基础上,多加上一重循环,让循环遍量k从1开始循环,循环到总重W即可

code

void solve(){
	int n,W;
	cin >> n >> W;
	for(int i=1;i<=n;++i){
		cin >> w[i] >> v[i] >> s[i];
	}
	for(int i=1;i<=n;++i)
	   for(int j=W;j>=w[i];--j)
	      for(int k=1;k*w[i]<=j && k<=s[i];++k){
	      	f[j]=max(f[j],f[j-k*w[i]]+k*v[i]);
		  }
    cout << f[W];
	return ;
}

很明显,这个代码的时间复杂度为 O ( n W k ) O(nWk) OnWk
当数据过大时,这个代码很容易超时
因此我们也就衍生出了二进制优化多重背包的算法

多重背包(二进制优化)

模板例题

点这里~~

思路

我们首先确认三点:

  • 我们知道转化成01背包的基本思路就是:判断每件物品我是取了你好呢还是不取你好
  • 我们知道任意一个实数可以由二进制数来表示,也就是 2 0 2 k 2^0 2^k 202k其中一项或几项的和
  • 这里多重背包问的就是每件物品取多少件可以获得最大价值

为什么可以进行二进制优化,我们拿苹果进行举例:
比如苹果总数为50,如果我们一个个拿,需要拿50次
假设说这时候有6个箱子,这6个箱子的容量为 1 , 2 , 4 , 8 , 16 , 19 1,2,4,8,16,19 1,2,4,8,16,19(为什么有19呢,因为剩下的总数不够32,所以将剩余的苹果单独拎出来)
那么我们就可以将这50个苹果分别装在这6个箱子里面,是不是比朴素的算法快很多?

二进制优化多重背包本质上就是将个数进行拆分,由于每个数都能用二进制数来表示,所以它的时间复杂度可以降低到 O ( n W l o g 2 s ) O(nWlog_2s) O(nWlog2s)
基于上述思想,我们只需要将每次拆分的物品个数和价值存入到数组中,最后套用01背包的模板即可

code

const int N=1e4+1010,M=2e3+5;
int v[N],w[N],f[M];
void solve(){
	int n,W;
	cin >> n >> W;
	int cnt=1;//统计数量
	for(int i=1;i<=n;++i){
		int a,b,s;
		cin >> a >> b >> s;
		for(int j=1;j<=s;j<<=1){
			w[cnt]=j*a;//存入重量
			v[cnt++]=j*b;//存入价值
			s-=j;//总个数减去二进制的个数
		}
		if(s){//若还有剩余
			w[cnt]=s*a;
			v[cnt++]=s*b;
		}
	}
	for(int i=1;i<cnt;++i)  //01背包的模板
	   for(int j=W;j>=w[i];--j){
	   	f[j]=max(f[j],f[j-w[i]]+v[i]);
	   }
    cout << f[W];
	return ;
}

二进制优化是将个数进行拆分,那么我们有没有其他更快的方法呢?
答案是有的:我们可以将体积进行拆分

多重背包(队列优化)

模板例题

点这里~~

思路

我们先来看一串数据:
在这里插入图片描述
从第二个物品开始,每个物品的价值跟物品的重量w有关系
就拿 f [ 9 ] 来说,跟他有关的是 f [ 7 ] 、 f [ 5 ] 、 f [ 3 ] f[9]来说,跟他有关的是f[7]、f[5]、f[3] f[9]来说,跟他有关的是f[7]f[5]f[3]这不是跟物品重量为2有很大关系吗,每次都相差物品重量的k倍关系
为什么没有1呢,因为我们可选的数量为3, 2 ∗ 3 = 6 , 而 9 > 6 2*3=6,而9>6 23=6,9>6说明9不可能由1转换而来,这时候队首的1就需要出队
那么我们就可以考虑用滑动窗口的思想来解决队首和队尾的问题
不了解滑动窗口可以先看这篇博客:传送门
首先定义两个指针hh(队首),tt(队尾)
然后开3个数组 q , f , g , q 数组用于存下标, f 数组存答案, g 数组表示的是 f 数组每次转移的状态 q,f,g,q数组用于存下标,f数组存答案,g数组表示的是f数组每次转移的状态 qfgq数组用于存下标,f数组存答案,g数组表示的是f数组每次转移的状态
当队首大于队尾时,说明队尾为空,不执行任何操作
若不为空,则需要判断当前重量k是否超出范围(个数s ∗ * 重量w),超出则需要将队首出队
接着判断当前状态f[k] 是否比队首的状态( g [ q [ h h ] ] ) + ( k − q [ h h ] ) / w ∗ v ) g[q[hh]])+(k-q[hh])/w*v) g[q[hh]])+(kq[hh])/wv)
k − q [ h h ] 代表剩余的体积,然后除以体积 w 乘上价值 v k-q[hh]代表剩余的体积,然后除以体积w乘上价值v kq[hh]代表剩余的体积,然后除以体积w乘上价值v
然后判断当前状态 g [ k ] g[k] g[k]是否比队尾元素大,若为真则将他们一一排除
然后将当前元素存入q数组中
每次循环都需要将f的状态转移为g数组,因此我们需要用到memcpy函数

看的不是很明白?接下来看代码慢慢食用

int q[N],g[N],f[N];
void solve(){
	int n,W;
	cin >> n >> W;
	for(int i=1;i<=n;++i){
		memcpy(g,f,sizeof f);//状态转移
		int w,v,s;
		cin >> w >> v >> s;
		for(int j=0;j<w;++j){
			int hh=0,tt=-1;//队首和队尾的指针
			for(int k=j;k<=W;k+=w){//每次都相差w倍的关系
				if(hh<=tt && q[hh]<k-s*w) hh++;//判断队首是否需要出队
				if(hh<=tt) f[k]=max(g[k],g[q[hh]]+(k-q[hh])/w*v);//判断当前状态和队首状态加上新的价值哪个更大
				while(hh<=tt && g[k]>=g[q[tt]]+(k-q[tt])/w*v) tt--;//判断当前状态是否比队尾的价值更大,更大的话将他们一一排出
				q[++tt]=k;//入队
			}
		}
	}
	cout << f[W];
	return ;
}

混合背包

模板例题

点这里~~

思路

这种题目看起来很吓人,可是只要领悟了前面几种背包的中心思想,并将其合并在一起就可以了
我们可以将他们分为两类:

  • 完全背包
  • 多重背包(01背包相当于k为1)

那么我们只需要分别处理上面两种类型即可
遇上完全背包就套完全背包的模板,遇上01背包和多重背包就套多重背包的模板
(注意:多重背包的朴素算法会超时,因此我们在这里考虑用二进制优化和队列优化的方法来做
接下来看代码:

code1

int f[N];
void solve(){
	int n,W;
	cin >> n >> W;
	for(int i=1;i<=n;++i){
		int w,v,s;
		cin >> w >> v >> s;
		if(s==0){//完全背包
		  for(int j=w;j<=W;++j){
		  	f[j]=max(f[j],f[j-w]+v);
		  }
		}
		else{
			if(s==-1) s=1;//01背包转换多重背包
			for(int k=1;k<=s;k<<=1){//二进制优化
				for(int j=W;j>=k*w;--j){
					f[j]=max(f[j],f[j-k*w]+k*v);//直接进行循环,比较大小
				}
				s-=k;//减去个数
			}
			if(s){
				for(int j=W;j>=s*w;--j){
					f[j]=max(f[j],f[j-s*w]+s*v);
				}
			}
		}
	}
	cout << f[W];
	return ;
}

如果套队列优化的多重背包的模板,实际上不需要分完全背包和多重背包
我们只需要将背包个数s设置为无穷大,这样每次都不会将队首出队,相当于无限放入物品(即完全背包)

code2

int q[N],f[N],g[N];
void solve(){
	int n,W;
    cin >> n >> W;
    for(int i=1;i<=n;++i){
    	memcpy(g,f,sizeof f);
    	int w,v,s;
    	cin >> w >> v >> s;
    	if(s==-1) s=1;
    	if(s==0) s=1e6;
    	for(int j=0;j<w;++j){
    		int hh=0,tt=-1;
    		for(int k=j;k<=W;k+=w){
    			if(hh<=tt && q[hh]<k-s*w) hh++;
    			if(hh<=tt) f[k]=max(g[k],g[q[hh]]+(k-q[hh])/w*v);
    			while(hh<=tt && g[k]>=g[q[tt]]+(k-q[tt])/w*v) tt--;
    			q[++tt]=k;
			}
		}
	}
	cout << f[W];
	return ;
}

二维费用背包

模板例题

点这里~~

思路

这种题型很明显是 0-1 背包问题的变形,可是不同的是选一个物品会消耗两种价值(容量、重量),只需在状态中增加一维存放第二种价值即可
注意:开三维存放物品编号就不合适了,因为容易 MLE,我们可以像01背包一样,将三维压缩成二维

code

int f[105][105];
void solve(){
	int n,W,M;
	cin >> n >> W >> M;
	for(int i=1;i<=n;++i){
		int w,m,v;
		cin >> w >> m >> v;
		for(int j=W;j>=w;--j)//和01背包一样,倒着遍历
		   for(int k=M;k>=m;--k){
		   	f[j][k]=max(f[j][k],f[j-w][k-m]+v);//相当于滚动数组,每次都在原来的状态进行新状态的转移
		   }
	}
	cout << f[W][M];
	return ;
}

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

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

相关文章

汇聚行业实践,树立应用典范——《Serverless应用实践案例集》重磅发布

云计算已经成为数字时代的基础设施&#xff0c;借助其规模效应实现资源的集约化利用&#xff0c;最大化发挥计算的价值。Serverless进一步优化了云服务供给模式&#xff0c;简化了云上应用的构建方式&#xff0c;代表了云计算的重要发展趋势。 2024年7月24日&#xff0c;2024可…

【Java】二维码生成工具

一、引入相关依赖 <!-- 引入Hutool工具库&#xff0c;简化Java开发&#xff0c;提高开发效率 --> <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.5</version> </dependency&…

LearnOpenGL之摄像机

前序 AndroidLearnOpenGL是本博主自己实现的LearnOpenGL练习集合&#xff1a; Github地址&#xff1a;https://github.com/wangyongyao1989/AndroidLearnOpenGL 系列文章&#xff1a; 1、LearnOpenGL之入门基础 2、LearnOpenGL之3D显示 3、LearnOpenGL之摄像机 4、LearnOpenG…

UNION ALL 在单个子查询中排序不生效问题

业务场景 有两张表&#xff1a;表A&#xff0c;和表B&#xff0c;需要对A中数据按排序字段排序&#xff0c;对B表也按排序字段排序&#xff0c;然后返回并集。 写出如下SQL&#xff08;已简化&#xff09;&#xff1a; (select id from A order by sort desc) union all (se…

《python语言程序设计》2018年版第6章31题调用time.time()返回从1970年1月1日0点开始显示当前日期和时间

我没按要求显示结果。但是内容差不都&#xff0c;关键。每个31日或者月底就时间出现偏差 # 之前已经做好的当前的小时、分、秒。 def currentTime_output():currentTime time.time()totalSeconds int(currentTime)currentSecond totalSeconds % 60totalMinutes totalSecon…

正点原子imx6ull-mini-Linux驱动之Linux USB 驱动实验

USB 是很常用的接口&#xff0c;目前大多数的设备都是 USB 接口的&#xff0c;比如鼠标、键盘、USB 摄像 头等&#xff0c;我们在实际开发中也常常遇到 USB 接口的设备&#xff0c;本章我们就来学习一下如何使能 Linux 内核自带的 USB 驱动。注意&#xff01;本章并不讲解具体的…

本阿弗莱克和詹妮弗洛佩兹两次婚恋的完整时间表 每次都轰轰烈烈也都无疾而终

本阿弗莱克和詹妮弗洛佩兹于 2002 年在《鸳鸯绑匪》片场首次相识&#xff0c;当时洛佩兹与她的第二任丈夫克里斯贾德于 2001 年 9 月结婚。当时&#xff0c;阿弗莱克与格温妮丝帕特洛分分合合。洛佩兹提出离婚&#xff0c;不久后与阿弗莱克首次亮相情侣档。 2002 年 11 月&…

JavaEE: 死锁问题详解(5000字)

文章目录 死锁的出现场景1. 一个线程一把锁,这个线程针对这把锁,连续加锁了两次2. 两个线程,两把锁3. N个线程 , M个锁4. 内存可见性为什么会出现内存可见性问题呢?解决方法 volatile关键字 总结synchronized:死锁的四个必要条件(缺一不可)[重点]:内存可见性问题: 死锁的出现场…

【iOS】暑假第二周——网易云APP 仿写

目录 前言首页关于UINavigationBarAppearance “我的”账号夜间模式——多界面传值遇到的问题所用到的其他知识整理NSNotificationreloadData各种键盘模式 总结 前言 有了之前仿写ZARA的基础&#xff0c;本周我们仿写了网易云APP&#xff0c;在这里对多界面传值进行了首次应用—…

LISA: Reasoning Segmentation via Large Language Model

发表时间&#xff1a;CVPR 2024 论文链接&#xff1a;https://openaccess.thecvf.com/content/CVPR2024/papers/Lai_LISA_Reasoning_Segmentation_via_Large_Language_Model_CVPR_2024_paper.pdf 作者单位&#xff1a;CUHK Motivation&#xff1a;尽管感知系统近年来取得了显…

基于SSH的医院在线挂号系统设计与实现

点击下载源码 基于SSH的医院在线挂号系统设计与实现 摘 要 互联网技术迅速的发展给我们的生活带来很大的方便&#xff0c;同时也让许多行业迅速的发展起来。互联网技术已走向科技发展的巅峰期&#xff0c;我们要做的就是合理的使用互联网技术让我们的各个行业得到更快速的发展…

2024杭电多校06——1005交通管控

补题点这里 大意 一个操作杆可以对k个红绿灯进行操作&#xff0c;操作杆上的一个字符对应一个红绿灯&#xff0c;操作包括,-,0,问每种组合方案有多少种组合方式 : red->green->yellow->red -:green->red->yellow->green 可以用一个三进制数表示每个灯的状态…

Python(模块---pandas+matplotlib+pyecharts)

import pandas as pd import matplotlib.pyplot as plt dfpd.read_excel(简易数据.xlsx) # print(df) plt.rcParams[font.sans-serif][SimHei] #设置画布的大小 plt.figure(figsize(10,6)) labelsdf[电影中文名] ydf[国籍] # print(labels) # print(y)# import pandas as pd im…

[Webpack]webpack-dev-server设置多个路径代理时,proxy顺序有要求

问题背景 前端需要调用多个不同的后台时需要使用devServer.proxy做代理 问题现象 如下图设置ETL相关接口路径代理之后 调用ETL后台接口时产生404报错 问题原因 devServer.proxy在解析代理路径并替换的时候是按顺序解析的&#xff0c;我配置的三个代理中&#xff0c;/csm…

NCL数据分析与处理实践技术

NCAR Command Language&#xff08;NCL&#xff09;是由美国大气研究中心&#xff08;NCAR&#xff09;推出的一款用于科学数据计算和可视化的免费软件。它有着非常强大的文件输入和输出功能&#xff0c;可读写netCDF-3、netCDF-4 classic、HDF4、binary、ASCII数据&#xff0c…

Linux之软硬链接和动静态库

个人主页&#xff1a;点我进入主页 专栏分类&#xff1a;C语言初阶 C语言进阶 数据结构初阶 Linux C初阶 算法 C进阶 欢迎大家点赞&#xff0c;评论&#xff0c;收藏。 一起努力&#xff0c;一起奔赴大厂 目录 一.软硬链接 1.1如何软硬链接 1.2软硬链接的作用 …

ViP-LLaVA: Making Large Multimodal Models Understand Arbitrary Visual Prompts

发表时间&#xff1a;cvpr2024 论文链接&#xff1a;https://readpaper.com/pdf-annotate/note?pdfId2357936887983293952&noteId2426262228488986112 作者单位&#xff1a;University of Wisconsin–Madison Motivation&#xff1a;现在的多模态模型都关注整张图像的理…

html+css网页设计 qq官网首页1个页面无js

htmlcss网页设计 qq官网首页1个页面无js功能 页面1:1还原 网页作品代码简单&#xff0c;可使用任意HTML编辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 …

冲击性信号的频域特征

这是一个信号采样数学实验&#xff0c;你可以直观感受到冲击信号的时域和频域特征 1.原始冲击信号&#xff1a; 原始信号是一个频率为180Hz附近的一个冲击性信号&#xff1a; 2.冲击信号频谱 它的频谱&#xff0c;可能会超出你的想象&#xff0c;它的1x频率幅度可能并不最高…

iOS ------ autoreleasePool

一&#xff0c;autoReleasePool{} int main(int argc, const char * argv[]) {autoreleasepool {}return 0; }我们平时创建一个main函数的代码的时候&#xff0c;就会发现其中有一个这个东西autoreleasepool{}&#xff0c;使用clang编译之后&#xff1a;autoreleasepool{…}被…