【动态规划】背包问题

news2024/11/24 2:31:05

目录

一:思路简介

二:0-1 背包

三:完全背包

四:多重背包

五:分组背包


一:思路简介

n 个物品,容量为V的背包

Vi 体积       Wi 价值(权重)

二:0-1 背包

每件物品最多只能用1次(要么0次,要么1次)

集合划分:f(i,j)=Max(f(i-1,j),f(i-1,j-Vi)+Wi)

0次(不含i):一定存在

1次(含i):仅当v<=j才可以

 暴力

#include <iostream>
using namespace std;
const int N = 1010;

int n, v;
int v[N], w[N];
int f[N][N];

int main() {
	scanf("%d%d", &n, &v);
	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 <= v; j++) {
			f[i][j] = f[i - 1][j];//默认不含i:0次
			if(j >= v[i]) f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);//当vi<=j,选最大的
		}
	}
	printf("%d", f[n][v]);
}

降维优化 

 (原先用二维数组表示状态,可以换成一维数组,用滚动数组(下一层只用到上一层来算,滚动交替计算)的方式。注:动态规划的优化,通常都是对代码或者状态转移方程,做等价变型)

  1. f(i,) 只用到了f(i-1,)所以提示可以用滚动数组;
  2. 两个函数第二个元素都<=j,所以可以改成一维数组;
  • 把i直接删掉;
  • j=0--vi无意义,所以删掉判断条件,j从vi开始;
  • 内循环从小到大计算的将是max(f[i,j],f[i,j-v[i]]+w[i]),所以改成从大到小循环时f(  ,j-v[i])还没有被更新,就会是f( i-1,j-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]);
		}
	}

三:完全背包

每件物品都有无限次  

 

 暴力 

#include <iostream>
using namespace std;
const int MAX = 1010;

int N, V;

int f[MAX][MAX];

int v[MAX], w[MAX];

int main() {
	scanf("%d%d", &N, &V);
	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 <= V; 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]);
		}
	}
	printf("%d", f[N][V]);
}

 优化类似01

根据上图的推导过程,我们实际上可以用2个状态来推导出 f ( i , j ) ,即 f ( i , j ) = m a x { f ( i − 1 , j ) , f ( i , j − v ) + w },此时f ( i , j )的推导就和 k 无关了。于是根据这个状态转移方程,我们写成代码如下


	for(int i = 1; i <= N; i++) {
		for(int j = 0; j <= V; 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背包的区别是内循环不用从大到小,因为上述第一次优化后f[i][j] =max(f[i][j], f[i][j - v[i]] + w[i])  不是f(i-1,    ),所以不用改循环。


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

四:多重背包

每个物品最多有Si个

多重背包的状态转移方程,和完全背包一致,如下

f ( i , j ) = m a x { f ( i − 1 , j − v [ i ] × k ) + k × v [ i ] } ,k ∈ [ 0 , s [ i ] ]

多重背包只是对每个物品,多了数量限制,而完全背包没有数量限制。

 暴力

#include <iostream>
using namespace std;
const int MAX = 1010;

int N, V;

int f[MAX][MAX];

int v[MAX], w[MAX], s[MAX];

int main() {
	scanf("%d%d", &N, &V);
	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 <= V; j++) {
			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]);
		}
	}
	printf("%d", f[N][V]);
}

优化方法一:采用完全背包的优化方法时:只有中间一部分是相同的,无法进行替换(X)

优化方法二:二进制,以s[i]=200为例:

首先1,2,4,8,16,32,64能够凑出到(64*2)-1=127,这是毋庸置疑的。而0到127种的任意一种组合,再额外加一个73,就能凑出73到200,所以上面的8个数就能凑出0到200中的任意一个数。

所以,对于物品i,共有s[i]个,其实我们可以把s[i]个物品,拆分成 log s[i],然后对这些新的物品,做一次01背包问题即可。

#include <iostream>

// 因为物品共有N=1000个,而每个物品的s[i]最大到2000,所以每个物品能拆成log(2000)≈11, 实际计算出来是小于11的,
// 所以拆分后的物品总数不超过 1000 * 11 = 11000, 所以我们的N开到11000即可
// 由于数组下标从1开始, 所以我们至少要开到11001
const int N = 11001;

int n, m;

int v[N], w[N], f[N];

int main() {
	scanf("%d%d", &n, &m);
	int cnt = 0;

	for(int i = 1; i <= n; i++) {
        // 处理输入, 将 s[i] 个物品拆分成 log(s[i]) 个
		int a, b, s;
		scanf("%d%d%d", &a, &b, &s);
		int k = 1;
		while(k <= s) {
			cnt++;
			v[cnt] = a * k;
			w[cnt] = b * k;
			s -= k;
			k *= 2;
		}
		if(s > 0) {
			cnt++;
			v[cnt] = a * s;
			w[cnt] = b * s;
		}
	}

	n = cnt; // 总共拆分成了多少个新的物品

    // 对新的物品, 做一次01背包问题, 这里直接写了一维数组优化后的01背包
	for(int i = 1; i <= n; i++) {
		for(int j = m; j >= v[i]; j--) {
			f[j] = std::max(f[j], f[j - v[i]] + w[i]);
		}
	}

	printf("%d", f[m]);
}

五:分组背包

n组,每组只能选1个

01背包的状态转移,是枚举第i个物品选或者不选;

完全背包和多重背包,是枚举第i个物品,选0,1,2,3,4,.... 个

而分组背包,枚举的是第i个分组,选哪一个,或者不选

分组背包的状态转移方程为:

f ( i , j ) = m a x { f ( i − 1 , j ) , f ( i − 1 , j − v [ i , k ] ) + w [ i , k ]},k ∈ [ 1 , s [ i ] ] 

其中 v [ i , k ]表示第 i 组中的第 k个物品的体积,w [ i , k ]同理

#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() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++) {
		scanf("%d", &s[i]);
		for(int j = 0; j < s[i]; j++) {
			scanf("%d%d", &v[i][j], &w[i][j]);
		}
	}

	for(int i = 1; i <= n; i++) {
		for(int j = m; j >= 0; j--) {
			for(int k = 0; k < s[i]; k++) {
				if(v[i][k] <= j) {
					f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);
				}
			}
		}
	}
	printf("%d", f[m]);
}


 

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

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

相关文章

给httprunnermanager接口自动化测试平台加点功能(一)

文章目录 一、背景1.1、部署过程略二、使用过程2.1、新增接口列2.2、实现搜索效果三、总结 一、背景 https://github.com/httprunner/HttpRunnerManager.git从github上找的接口测试平台&#xff0c;引入公司作为测试协同测试的平台&#xff0c;底层框架基于httprunner(requests…

【单目标优化算法】杂草优化算法(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

这些使用工具大推荐,现在知道不晚

1.Snip Snip是一款截图软件&#xff0c;它突出的优点就是可以制作滚动截图。 例如&#xff1a;对整个网页进行截图&#xff0c;使用Snip即可轻松获取&#xff0c;无需处理水印。 2.Sleep Cycle 快节奏、高压力的生活导致我们越来越晚睡觉&#xff0c;睡眠质量越来越差。 想提…

Python学习9:对指定r计算圆的面积(python123)

平台&#xff1a;python123 题目描述: 编写函数getCircleArea(r),对给定的参数r计算圆的面积&#xff0c;并返回首先读入n&#xff08;n>0&#xff09;&#xff0c;然后依次读入n个半径r1,r2,...,rn,以这些半径为参数依次调用getCircleArea函数&#xff0c;得到对应圆的面…

3.动态规划(0x3f:从周赛中学算法 2022下)

来自0x3f 【从周赛中学算法 - 2022 年周赛题目总结&#xff08;下篇&#xff09;】&#xff1a;https://leetcode.cn/circle/discuss/WR1MJP/ 【【灵茶山艾府】2022 年周赛题目总结&#xff08;上篇&#xff09;】https://leetcode.cn/circle/discuss/G0n5iY/ 学习动态规划是否…

( 栈和队列) 503. 下一个更大元素 II ——【Leetcode每日一题】

❓503. 下一个更大元素 II 难度&#xff1a;中等 给定一个循环数组 nums &#xff08; nums[nums.length - 1] 的下一个元素是 nums[0] &#xff09;&#xff0c;返回 nums 中每个元素的 下一个更大元素 。 数字 x 的 下一个更大的元素 是按数组遍历顺序&#xff0c;这个数字…

为何越来越多人不喜欢“试用期六个月”的公司?网友:感觉不靠谱

众所周知&#xff0c;任何一份工作都有试用期&#xff0c;一般是三月左右。但如果你遇到试用期达到半年的公司&#xff0c;你会不会进入&#xff1f; 近日&#xff0c;就有人遇到了此类公司&#xff0c;并对是否要进入该公司犹豫不决。他在论坛上发帖求助&#xff1a;大家是怎…

京城、京味、京韵:从一台服务器看数字北京

北京&#xff0c;既是首善之都&#xff0c;也是数字化创新之城。 早在1999年&#xff0c;北京就基于整座城市的信息化建设方案&#xff0c;率先提出了“数字北京”。后来&#xff0c;数字北京的魅力在奥运会期间大放异彩&#xff0c;受到了全球高度认可。如今&#xff0c;数字经…

【Python】【进阶篇】10、Django中间件

目录 Django中间件1. Django默认自带中间件1&#xff09;中间的执行与响应顺序2&#xff09;在调用视图之前3&#xff09;在调用视图之后 2. 中间件的作用总结 Django中间件 中间件是一个插件系统&#xff0c;嵌入在 Django 的 Request 和 Response 之间执行&#xff0c;可以对…

使用@Bean注解指定初始化和销毁的方法

bean的生命周期 通常意义上讲的bean的生命周期&#xff0c;指的是bean从创建到初始化&#xff0c;经过一系列的流程&#xff0c;最终销毁的过程。只不过&#xff0c;在Spring中&#xff0c;bean的生命周期是由Spring容器来管理的。在Spring中&#xff0c;我们可以自己来指定be…

apple pencil有买的必要吗?便宜的平替电容笔推荐

在当今世界&#xff0c;电容笔就已经成为一种热门的电子产品&#xff0c;其的各项性能也在不断改进。因此&#xff0c;如何挑选一款性价比高的电容笔成为大家关心的焦点&#xff0c;越来越多的人开始追求更好更廉价的电容笔。那么&#xff0c;哪个品牌的电容笔价格更实惠、性价…

工业设备巨头MSC Industrial Supply的供应链建设——EDI

MSC Industrial Supply提供广泛的工业用品和解决方案&#xff0c;包括切削工具、测量工具、金属加工和设备维护工具、劳动保护用品、工业设备等。MSC Industrial Supply的供应商来自全球各地&#xff0c;包括多个行业的领先品牌&#xff0c;例如Kennametal、Sandvik Coromant、…

【图像分割】【深度学习】SAM官方Pytorch代码-Prompt encoder模块ProEnco网络解析

【图像分割】【深度学习】SAM官方Pytorch代码-Prompt encoder模块PromptEncoder网络解析 Segment Anything&#xff1a;建立了迄今为止最大的分割数据集&#xff0c;在1100万张图像上有超过1亿个掩码&#xff0c;模型的设计和训练是灵活的&#xff0c;其重要的特点是Zero-shot(…

北邮22信通:二叉树层序遍历的非递归算法:A Story Between Two Templates

北邮22信通一枚~ 跟随课程进度每周更新数据结构与算法的代码和文章 持续关注作者 解锁更多邮苑信通专属代码~ 获取更多文章 请访问专栏~ 北邮22信通_青山如墨雨如画的博客-CSDN博客 目录 一.总纲 二.用队列存储 2.1用模板类实现队列 2.1.1核心思路&#xff1a; …

丁鹿学堂:使用vite手动构建vue项目的注意事项和步骤总结

使用yarn 默认安装了nodeJS环境&#xff0c;使用yarn&#xff0c;比npm更好用。 npm install --global yarn使用yarn按钻过vite yarn add -D vite使用yarn初始化项目 yarn init -y安装vite yarn add vite -D安装vue yarn add vue项目目录&#xff1a; 创建index.html sr…

分享一个有意思的文字飞入动画(模仿水滴融合)

先上效果图&#xff1a; 代码如下&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><style>* {margin: 0;padding: 0;box-sizing: border-box;}:root {--text-…

前后端分离demo 旅馆管理系统(Angular+Springboot)

模型设计 旅馆管理系统&#xff0c;主要涉及到登记入住&#xff0c;退房以及客房和客人信息管理&#xff1b;经过分析抽像出涉及到的实体以及各实体之间的关系&#xff1a;   可以看出整个业务以客房为中心&#xff0c;入住&#xff0c;退房&#xff0c;定价&#xff0c;收费…

Build an SAP Fiori App(一)后面更新中

1.登录 SAP BTP Trial 地址&#xff1a; https://account.hanatrial.ondemand.com 流程可以参考 点击 serviced marketplace 搜索studio 点击创建 点击创建&#xff0c;点击view subscription 点击go to application 创建完成后 添加新链接 Field Value Name ES5 - if you’…

Shell基础入门实战

写在前面 好久没在项目内做自动化了&#xff0c;主要是现阶段在项目内做自动化收益不大&#xff0c;最近开发做batch run的正好缺人&#xff0c;我看了一下代码&#xff0c;就是通过代码读取jar包和远程服务器连接&#xff0c;然后通过shell脚本&#xff0c;向数据库插入数据&a…

如何成为一名优秀的接口自动化测试工程师?了解这些技能是关键

摘要&#xff1a; 随着互联网行业的不断发展&#xff0c;越来越多的应用程序通过API接口提供服务。因此&#xff0c;接口自动化测试成为了保障软件质量的重要环节。本文将介绍接口自动化测试所需掌握的技能&#xff0c;以及相关的历史进程。 B站首推&#xff01;2023最详细自…