首页 > 其他分享 >【动态规划1】01背包,完全背包与多重背包

【动态规划1】01背包,完全背包与多重背包

时间:2022-09-20 15:22:24浏览次数:51  
标签:多重 背包 weight int dfs 01 物品 dp

1. 01背包

01背包就是指背包里面每种物品只能用一次,在有限的空间内求装入物品的最大价值的问题。

1. 题面:

有一容量为m的背包。现在要从n件物品中选取若干装入背包中,每件物品i的重量为w[i],价值为p[i],定义一种可行的背包装载为:背包中物品的总重量不能超过背包的容量,并且一个物品要么全部选取,要么不选取。定义最佳装载是指所装入的物品价值最高,并且是可行的背包装载。

输入输出:
样例输入                         样例输出
11 (背包容量)                  23(总价值)
4(物品数量)
2 4 6 7(物品重量)
6 10 12 13(物品价值)

2. 递归解法

考虑到这个题目,用贪心算法显然是鼠目寸光的。所以思考其他思路。自顶向下地看这个题目,对于第n件物品而言,无非有两个状态(选择):取或者不取。如果取,在获得这件物品价值地同时,也要承担其重量,如果不取,就无需承担,当然也得不到它的价值。满足无后效性。那么,这种问题就可以递归地求解了。

//返回第n件物品的最大价值,weight为背包剩余容量
  	int dfs(int n,int weight) {
	if(n==0||weight==0) return 0;//递归终止条件
  		int a = dfs(n-1,weight]);//不取,直接返回n-1的状态,不消耗weight
		int b = dfs(n-1,weight-w[n])+v[n];//取,消耗掉w[n]空间的同时获得了v[n]的价值
		return a>b?a:b;//返回最大值
	}

3. 优化递归:记忆化搜索

对于上述递归解法,每求一次dfs(n)就要用到很多次dfs(2),dfs(3)等,而这些必须用一次算一次,增加了时间复杂度。所以,我们考虑使用一个数组把他们存起来,即记忆化搜索,又叫备忘录方法,这样就可以减小时间复杂度。

int dp[100005];
int dfs(int n,int weight) {
	if(n==0||weight==0) return 0;//递归终止条件
	if(dp[n]!=-1) return dp[n];
  	int a = dfs(n-1,weight]);//不取,直接返回n-1的状态,不消耗weight
	int b = dfs(n-1,weight-w[n])+v[n];//取,消耗掉w[n]空间的同时获得了v[n]的价值
	if(a>b) {
		dp[n] = a;
	}else dp[n] = b;
	return dp[n];//返回最大值
}

4.动态规划

对于这类无后效性,备忘录的问题,使用动态规划显然是最佳解法。对于这个题,我们使用f[i][j]作为取第i件物品时容量为j的最大利润,即可推导出状态转移方程:
f[i][j] = max(f[i-1][j],f[i-1][j-w[i]]+v[i])
由此即可写出基本代码:

for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (w[i] > j) {
                f[i][j] = f[i - 1][j];
            }
            else {
                f[i][j] = f[i - 1][j] > f[i - 1][j - w[i]] + v[i] ? f[i - 1][j] : f[i - 1][j - w[i]] + v[i];
            }
        }
   }

5.滚动数组

在这个过程中,我们发现所要的仅仅是f[n][m],即n个物品容量为最大容量m时的情况,而前面n-1的数组迭代都是无意义的。因此,可以用一维数组代替二维数组,进一步优化空间复杂度。此时的状态转移方程如下:
f[j] = max(f[j],f[j-w[i]]+v[i])
此时得到的代码:

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

这里代码中的第二行使用了由后向前的遍历方式,而完全背包问题则恰好相反,是由前到后的遍历方式。举个例子,若 j=4:
f[4] = max(f[4],f[4-w[i]]+v[i]);
因为是由后到前,我们再看遍历到j=2时的情况:
f[2] = max(f[2],f[2-w[i])+v[i]);
若w[i]=2,初始化之后,因为f被置为0,先算w[4]时w[2]是为0的,这样就避免了f[2]被多次相加的问题。反之,若是完全背包问题,先计算f[2],f[2]的值已经不为0,此时计算f[4] = max(f[4],f[2]+v[i]);这样j每翻一倍,f[4]就也会翻一倍,符合完全背包问题的要求。

最简单的例子如洛谷采药,疯狂的采药,又如蓝桥杯2018年的分包子问题,2021年的天平问题等。

标签:多重,背包,weight,int,dfs,01,物品,dp
From: https://www.cnblogs.com/jianghanqed/p/16107536.html

相关文章

  • 【HMS Core】使用视频编辑AI能力SDK报错2012
    ​问题描述在使用视频编辑AI能力SDK报错20128详细报错信息2022-09-0516:16:48.3065571-6445/cn.c7cloud.ShortVideoAlbumE/FileUtil:IOException:java.io.FileNotF......
  • 前端面试总结01-html与css
    html:(1)语义化标签的理解:1.增加代码的可读性2.让搜索引擎更容易读懂(2)块级元素与内联元素的标签与区别1.块状元素:display:block/table;常用标签:div,h1,h2,table,ul......
  • coco2017 Dataset EDA
    Github仓库:gy-7/coco_EDA(github.com)对coco数据集的分析,近期忙着写论文,空余时间很少能写博文了。EDA的代码放在结尾了,Github仓库里也有。仓库里还有其他的一些EDA分析,......
  • 背包dp
    0x0101背包问题题目描述:每个物品最多只能选一次0x02完全背包问题题目描述:每个物品能选无数次,并且每个物品有无限件0x03多重背包问题题目描述:每个物品能选无数......
  • P5658 CSP-S2019括号树
    [CSP-S2019]括号树(傻逼绿题题目背景本题中合法括号串的定义如下:()是合法括号串。如果A是合法括号串,则(A)是合法括号串。如果A,B是合法括号串,则AB是合法括......
  • NumPy科学计算库学习_012_NumPy数组中的线性代数
    一、定义数组importnumpyasnpA=np.array([[4,2,3],[1,3,1]])B=np.array([[2,7],[-5,-7],[9,3]])print("【矩阵A】\n",A)print("【矩阵B】\n",B)【矩阵A】[......
  • 2022软工K班个人编程任务01提取疫情数据
    @目录一、PSP表格(2.1)在开始实现程序之前,在附录提供的PSP表格记录下你估计将在程序的各个模块的开发上耗费的时间。(2.2)在你实现完程序之后,在附录提供的PSP表格记录下你......
  • JavaLearnDay01
    Java语言名词解释:1.JVM(JavaVirtualMachine):Java虚拟机,用以不同平台,模拟相同的执行环境2.JRE(JavaRuntimeEnvironment):Java运行环境,包含JVM+解释器3.JDK(JavaDevelopmen......
  • 01-最大子列和问题
    最大子列和问题概述本文主要讲解最大子列和问题的求解什么是最大子列和问题?有一个整数数组{A1,A2,A3,...,An}求函数f(i,j)=max(0,∑Ak),k∈[i,j]的值简单来说,求一......
  • 01-物体检测方法概述
    1.物体检测的派系2.传统方法 3.基于锚框的物体检测算法4.无需锚框的物体检测算法 5.物体检测常用数据集   5.1通用物体检测数据集    ......