动态规划之——背包DP(完结篇)

news2024/11/22 19:10:48

文章目录

  • 概要说明
  • 分组背包
    • 模板例题1
    • 思路
    • code
    • 模板例题2
    • 思路
    • code
  • 有依赖的背包问题
    • 模板例题
    • 思路
    • code
  • 背包问题求方案数
    • 模板例题
    • 思路
    • code
  • 背包问题求具体方案
    • 模板例题
    • 思路
    • code

概要说明

本文讲分组背包、有依赖的背包、 背包问题求方案数以及背包问题求具体方案
入门篇(01背包和完全背包问题):传送门
进阶篇(多重、混合、二维费用背包):传送门

分组背包

模板例题1

acwing-分组背包问题

思路

分组背包也是01背包的一个变形,01背包中我们有n个物品,在这题中我们有n个组别,每个组别有k个物品
因此我们可以对每个组别都进行01背包,在当前组别选出最优解的情况下在进行下一轮组别的01背包
它的状态转移方程和01背包一样,在01背包的基础上改动一点点即可
它的时间复杂度在 O ( n 3 ) O(n^3) O(n3)

code

const int N=1e3+5;
int cnt[N],f[N],w[N][N],v[N][N];
void solve(){
	int W,n,t=1;
	cin >> n >> W;
	for(int i=1;i<=n;++i){
		cin >> cnt[i];
		for(int j=1;j<=cnt[i];++j){
		   cin >> w[i][j] >> v[i][j];
		}		 		
	}
	for(int i=1;i<=n;++i)   
	   for(int j=W;j>=0;--j)
	      for(int k=1;k<=cnt[i];++k){
	      	if(j>=w[i][k]){
	      		f[j]=max(f[j],f[j-w[i][k]]+v[i][k]);
			  }
		  }
	cout << f[W];
	return ;
}

模板例题2

通天之分组背包

思路

这题在模板例题1的基础上稍微变动了一点点,它没有给你确切的组数
因此我们需要先找出最大的组数,并另开一个数组存当前组数的下标
剩下的和模板例题1差不多,代码如下:

code

const int N=1e3+5;
int cnt[N],f[N],w[N],v[N],g[N][N];//g数组中,行存的是组别,列存的是个数,它的取值存的是下标
void solve(){
	int W,n,t=0;
	cin >> W >> n;
	for(int i=1;i<=n;++i){
		int x;
		cin >> w[i] >> v[i] >> x;
		cnt[x]++;
		t=max(t,x);
		g[x][cnt[x]]=i;
	}
	for(int i=1;i<=t;++i)   
	   for(int j=W;j>=0;--j)
	      for(int k=1;k<=cnt[i];++k){
	      	int st=g[i][k];//取出当前组别的下标
	      	if(j>=w[st]){
	      		f[j]=max(f[j],f[j-w[st]]+v[st]);
			  }
		  }
	cout << f[W];
	return ;
}

有依赖的背包问题

模板例题

有依赖的背包问题

思路

首先我们明确一点:想要取出子节点必须取出父节点
那么想要价值尽可能大,我们必须倒着遍历这颗树
遍历树最基本的算法是什么呢?
很显然,我们很快就能想到dfs回溯的思想,先来看这张图:
在这里插入图片描述

先看左边这条线1-2-4,我们将这颗树的节点分开来看,4的父节点为2,2的父节点1
那么我们想取出4的价值,首先必须取出2的价值,我们想取出2的价值,首先必须取出1的价值

我们倒着遍历树,当我们遍历到4时,发现4没有子节点,这时我们就回溯到前一个状态2
在回溯之前我们将4的价值存入到一个数组中,这时我们考虑状态2,它有2种选择,选或者不选
这时候它的状态转移方程和01背包是一样的

注意:在选状态4物品之前需要减去状态2的重量,因为状态2必须先选,才能选状态4

接着我们将选完的状态回溯到前一个状态1,同理,我们在选物品之前需要先减去状态1的重量
然后它也是有2种选择,选或者不选

对于其他线路也是同理,最终都会回溯到状态1,那么我们只需要从状态1开始进行dfs,遍历到树的叶节点时开始回溯即可

讲完思路,接着讲一下如何用代码实现出来
首先我们需要标记根节点的位置,用vector建树,将子节点存入父节点中
开一个二维数组,行代表节点,列代表重量w,所存的值为价值v

将根节点进行dfs,将当前 f [ x ] [ w − W ] 都标记为 v , x 代表节点, w 代表重量, W 为背包容量, v 为价值 f[x][w-W]都标记为v,x代表节点,w代表重量,W为背包容量,v为价值 f[x][wW]都标记为vx代表节点,w代表重量,W为背包容量,v为价值
这样方便我们递归回溯时进行状态转移
接着我们一直遍历当前节点的子节点,将子节点进行dfs循环,直到不能遍历为止
这样我们就像上面所说的,遍历到4不能遍历,回溯到2的状态
接着进行01背包的状态转移方程,当前循环结束,在回溯到上一次的状态
最终dfs循环结束,输出 f [ r o o t ] [ W ] f[root][W] f[root][W]

接下来我们看代码:

code

const int N=1e3+5;
int w[N],v[N],f[N][N];
vector<int> g[N];
int n,W;
void dfs(int x){
	for(int i=w[x];i<=W;++i) f[x][i]=v[x];//将w~W的重量都标记为v
	for(auto y : g[x]){
		dfs(y);//一直循环子节点,直到不能循环为止
		for(int j=W;j>=w[x];--j)
		   for(int k=0;k<=j-w[x];++k){//所选的物品必须减去当前重量
		   	f[x][j]=max(f[x][j],f[x][j-k]+f[y][k]);//选子节点的物品还是不选子节点的物品
		   }
	}
}
void solve(){
	cin >> n >> W;
	int root;
	for(int i=1;i<=n;++i){
		int x;
		cin >> w[i] >> v[i] >> x;
		if(x==-1) root=i;//标记
		else g[x].push_back(i);//建树
	}
	dfs(root);
	cout << f[root][W];
	return ;
}

背包问题求方案数

模板例题

背包问题求方案数

思路

这种类型的题目不需要我们求具体的价值,但是需要我们求最优价值的方案总数
那么我们首先还是需要算出最大的价值,然后将能到达当前价值的方案数进行相加

那么具体该如何实现呢?
我们需要在01背包的基础上,多开一个g数组,用于统计方案数
对于每步的状态来源,我们都有两种情况,一种是本身,一种是当前容量减去物品容量加上物品价值
这时我们新开一个变量temp,这个变量统计两种情况的最大值
若temp等于本身,那么 g [ i ] g[i] g[i]加上它本身
若temp等于当前容量减去物品容量加上物品价值,那么 g [ i ] + g [ j − w [ i ] g[i]+g[j-w[i] g[i]+g[jw[i]
每次加上都记得要取模
然后将当前状态 f [ j ] f[j] f[j]更新为temp
最后找出背包所能容量的最大价值,遍历f数组,若当前 f [ i ] f[i] f[i]等于最大价值,加上 g [ i ] g[i] g[i]

接下来看代码进一步理解

code

const int N=1e3+5;
int f[N],g[N],w[N],v[N];
void solve(){
	int n,W;
	cin >> n >> W;
	for(int i=1;i<=n;++i){
		cin >> w[i] >> v[i];
	}
	g[0]=1;//若不选物品,方案数为1
	for(int i=1;i<=n;++i)
	   for(int j=W;j>=w[i];--j){
	   	  int temp=max(f[j],f[j-w[i]]+v[i]);//temp为当前状态的最大值
	   	  int c=0;//统计数量
	   	  if(temp==f[j]) c=(c+g[j])%mod;//加上方案数
	   	  if(temp==f[j-w[i]]+v[i]) c=(c+g[j-w[i]])%mod;
	   	  f[j]=temp,g[j]=c;//状态转移
	   }
	int maxn=0;
	for(int i=0;i<=W;++i) maxn=max(maxn,f[i]);//找到最大价值
	int ans=0;
	for(int i=0;i<=W;++i){
		if(f[i]==maxn) ans=(ans+g[i])%mod;//统计个数
	}
	cout << ans;
	return ;
}

背包问题求具体方案

模板例题

背包问题求具体方案

思路

首先我们需要回溯到原来的状态,这时候我们开一维数组就会丢失原来的状态,这时候必须开二维数组来存储数据
我们先考虑一个问题,我们是从前往后回溯,还是从后往前回溯呢?
答案很明显,我们需要从前往后回溯
为什么呢?
题目要求输出字典序最小的方案

我们拿一个例子来说明:
3 3
1 2
2 4
2 4

物品总数为3,背包容量为3,每个物品先输入重量,在输入价值
如果我们从后往前回溯,那么我们求出的具体方案为1 3(最后将数组颠倒一下)
可是答案很明显为1 2,这样字典序才是最小的

那么我们就将这种方法pass掉

那么我们一开始需要第n件物品的状态转移到第1件物品,这样 f [ 1 ] [ W ] f[1][W] f[1][W]就为最大的价值
首先还是先套01背包的模板,只不过从1开始变为从n开始
然后我们从序号1遍历到序号n,判断当前的状态是不是由当前价值加上上一个状态转移过来的,即 f [ i ] [ j ] = = f [ i + 1 ] [ j − w [ i ] ] + v [ i ] f[i][j]==f[i+1][j-w[i]]+v[i] f[i][j]==f[i+1][jw[i]]+v[i]
若满足,则当前背包容量减去 w [ i ] w[i] w[i],直接输出当前下标即可

接下来看代码

code

const int N=1e3+5;
int f[N][N],v[N],w[N],cnt[N];
void solve(){
	int n,W;
	cin >> n >> W;
	for(int i=1;i<=n;++i){
		cin >> w[i] >> v[i];
	}
	for(int i=n;i>=1;--i)//倒着来
	   for(int j=0;j<=W;++j){
	   	f[i][j]=f[i+1][j];
	   	if(j>=w[i]) f[i][j]=max(f[i][j],f[i+1][j-w[i]]+v[i]);
	   }
	for(int i=1,j=W;i<=n;++i){
		if(j>=w[i] && f[i][j]==f[i+1][j-w[i]]+v[i]){//判断状态
			cout << i << " ";
			j-=w[i];
		}
	}
	return ;
}

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

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

相关文章

JavaEE 第7节 线程饥饿及其解决办法

目录 一、什么是线程饥饿&#xff1f; 二、线程饥饿的解决办法 *wait()与notify()方法解决线程饥饿 1、wait(等待) 2、notify(通知) 1&#xff09;notify 2&#xff09;notifyAll 3&#xff09;关于wait方法的一些补充 1、wait的方法的三个功能是原子性的&#xff1a;…

力扣刷题-环形链表判断是否有环

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 首先&#xff0c;我们先来看一下这段代码&#xff1a; /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ bool …

【RISC-V设计-10】- RISC-V处理器设计K0A之IDU

【RISC-V设计-10】- RISC-V处理器设计K0A之IDU 文章目录 【RISC-V设计-10】- RISC-V处理器设计K0A之IDU1.简介2.顶层设计3.端口说明4.代码设计5.总结 1.简介 指令译码单元&#xff08;Instruction Decoder Unit&#xff0c;简称IDU&#xff09;是CPU中的一个关键组件&#xff…

用Vue和Axios将数据库数据显示在前端页面

在本次实例中Vue只用在了前端部分&#xff0c;Axios用于向后端请求数据&#xff0c;我们这里要用到Ajax技术来访问后端数据。 HTML&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name&quo…

CTF-RCE

eval执行 ?cmdsystemctl("ls"); ?cmdsystemctl("ls /"); ?cmdsystemctl("cat /flag_27523); 命令注入 输入ip试试发先可以执行 127.0.0.1 查看一下看看有社么 127.0.0.1 | ls 试着看看php文件 127.0.0.1 | cat 297581345892.php 貌似这个文件有…

韩式告白土味情话-柯桥生活韩语学习零基础入门教学

你们韩国人别太会告白了&#xff01; 1、너 얼굴에 뭐가 조금 묻었어! 你的脸上有点5376东西&#xff01; 뭐가 조금 묻었1585757는데? 有点什么&#xff1f; 이쁨이 조금 묻었네. 有点漂亮。 2、돌잡이 때 뭐 잡았어요&#xff1f; 你抓周的时候抓了什么&#xff1f; 쌀 잡았…

打开一个页面,整个过程会使用哪些协议?

打开一个页面&#xff0c;整个过程会使用哪些协议? 网络通信模型可以用下图来简单表示&#xff0c;根据下面这个顺序&#xff0c;我们来说明&#xff0c;打开一个页面&#xff0c;整个过程会使用哪些协议? 首先&#xff0c;我们可以梳理一个简单的完整流程: 1.在浏览器中输…

Postman Pre-request Script

这个其实是普通的js脚本&#xff0c;有一些和postman的通信他也提供了一些快捷命令如下 postman常用参数使用 环境变量 //设置当前环境变量 pm.environment.set("key", "value"); //获取当前环境变量 pm.environment.get("key"); //清除当前…

软件测试面试题汇总,超详细整理。。。

测试技术面试题 1、什么是兼容性测试&#xff1f;兼容性测试侧重哪些方面&#xff1f; 参考答案&#xff1a; 兼容测试主要是检查软件在不同的硬件平台、软件平台上是否可以正常的运行&#xff0c;即是通常说的软件的可移植性。 兼容的类型&#xff0c;如果细分的话&#x…

【Windows】如何关闭Windows11安全中心中的“病毒和威胁保护”?

按下“win&#xff08;徽标键&#xff09;i”快捷键&#xff0c;选择隐私与安全性-Windows安全中心。 选择防火墙和网络保护-域保护。 将开关闭&#xff0c;专业网络和公用网络防火墙也同样关闭&#xff0c;如下图所示&#xff1a; 关闭防火墙后&#xff0c;左边菜单栏选…

函数的学习(三)

1.函数的声明和定义 在C语言中&#xff0c;函数的声明和定义是分开的。 函数的声明是指在程序中提前告诉编译器有一个函数存在&#xff0c;并且指定了函数的名称、参数类型和返回值类型。函数的声明一般放在头文件中&#xff0c;它的作用是告诉编译器有一个函数存在&#xff…

学生综合测评、学生测评管理系统的设计与实现

摘要 学生综合测评是学校必不可少的一个部分。在教学中&#xff0c;学生综合测评担负着最重要的角色。为满足如今日益复杂的管理需求&#xff0c;各类学生综合测评也在不断改进。本课题所设计的学生综合测评&#xff0c;使用JSP技术与MySql数据库进行开发&#xff0c;它的优点代…

快速上手的企业视频会议系统需要具备哪些能力

随着企业规模的扩张&#xff0c;行政会议的增多以及企业的复杂性和地域分散性导致的信息传递周期过长问题&#xff0c;对企业运营效率和成本产生了负面影响。为了解决这些问题&#xff0c;许多企业开始采用视频会议系统以实现即时沟通并降低差旅开支。视频会议系统在远程沟通方…

牛客JS题(二十九) Map保存节点

注释很详细&#xff0c;直接上代码 涉及知识点&#xff1a; map的基本使用forEach的注意点 题干&#xff1a; 我的答案 <!DOCTYPE html> <html><head><meta charset"utf-8" /></head><body><p>1</p><p>2<…

24.8.9数据结构|链栈和队列

链栈 1、理解 实际上是一个仅在表头进行操作的单链表,头指针指向栈顶结点或头结点,以下恋栈均指带头结点的链栈. 2、 基本操作 1、定义结构&#xff1a;节点含有数据域和指针域 2、初始化操作&#xff1a;建立一个带头结点的空栈 3、取栈顶元素操作&#xff1a;取出栈的栈顶元…

【最新】推荐6款论文ai写论文软件推荐网站平台

在当前的学术研究和写作环境中&#xff0c;AI写作工具已经成为许多学者和学生的重要助手。这些工具不仅能够提高写作效率&#xff0c;还能帮助优化文章结构、润色语言以及进行查重等任务。以下将推荐六款优秀的AI写论文软件&#xff0c;并详细介绍它们的功能和特点。 1. 千笔-…

Spring及相关框架的重要的问题

Java框架 问题一&#xff1a;Spring框架中的单例bean是线程安全的吗&#xff1f; 看下图&#xff0c;不能被修改的成员变量就是无状态的类&#xff0c;无状态的类没有线程安全问题&#xff0c;所以在开发中尽量避免可修改的成员变量。 回答&#xff1a;不是线程安全的&#xf…

Drizzle ORM使用Azure PostgreSQL数据库迁移失败

执行 npx drizzle-kit migrate 命令报错&#xff1a; No config path provided, using default drizzle.config.ts Reading config file E:\nextjs-cloudflare-drizzle\drizzle.config.ts Using postgres driver for database querying [⡿] applying migrations...PostgresEr…

Intersection Observer API 帮你搞定前端滚动问题

前言 当我们在做需求时&#xff0c;可能经常会遇到很多跟页面的滚动有关的需求。例如 图片的懒加载&#xff1a;我们希望只加载用户当前视图窗口的图片&#xff0c;而未进入到视图窗口的图片&#xff0c;只有在进入到视图窗口时才进行加载&#xff0c;以提高页面响应速度&…

简单报表示例

简单报表 概述 简单表格报表&#xff1a;简单的表格报表。 应用场景 如下图所示&#xff0c;简单展示数据 示例说明 数据准备 在数据面板中添加数据集&#xff0c;可选择Json数据集和API服务数据集。Json数据集输入如下图所示&#xff1a; [{"到货日期":&qu…