首页 > 其他分享 >01背包问题

01背包问题

时间:2024-02-05 11:11:07浏览次数:25  
标签:01 int void dfs 问题 -- 背包 物品 dp

题目描述

有 \(n\) 个重量和价值分别为 \(w_i\),\(v_i\) 的物品。从这些物品中挑选出总重量不超过 \(W\) 的物品,求所有挑选方案中价值总和的最大值。
数据范围
\(1\le n\le100\)
\(1\le w_i,v_i\le100\)
\(1\le W\le10000\)


记忆化搜索

暴力搜索

递归方程:令 \(dfs(i,j)=\) 从编号为 \(i\) 的物品开始挑选出总重量不超过 \(j\) 的物品,所有挑选方案中价值总和的最大值。(编号从 0 开始,最后一个物品编号为 \(n-1\))

  • 核心思想:对于编号为 \(i\) 的物品,可以选择 不拿 或者
    不拿:相当于从下一个物品(编号为 \(i+1\))开始挑选总重量仍然不超过 \(j\) 的物品,即 \(dfs(i+1,j)\)。
    :由于拿了重量为 \(w_i\) 的物品,则总重量由 \(j\) 变成了 \(j-w_i\)。相当于从下一个物品(编号为 \(i+1\))开始挑选总重量不超过 \(j-w_i\) 的物品,即 \(dfs(i+1,j-w_i)\)。
  • 方程:

\[\begin{split} dfs(n,j)&=0 \\ dfs(i,j)&=\begin{cases} dfs(i+1,j)&,j<w_i \\ max\{dfs(i+1,j),dfs(i+1,j-w_i)+v_i\}&,j\ge w_i \end{cases} \end{split} \]

  • 代码
// 输入
int n, W;                // n -- 物品个数;W -- 最大重量
int w[WMAX], v[VMAX];    // w[WMAX] -- 物品重量;v[VMAX] -- 物品价值

// dfs(i, j) -- 从编号为i开始挑选总重不超过j的物品,返回最大价值
int dfs(int i, int j)
{
	if (i == n)
		return 0;
	
	int ret;
	if (j < w[i])
		ret = dfs(i + 1, j);
	else
		ret = max(dfs(i + 1, j), dfs(i + 1, j - w[i]) + v[i]);
		
	return ret;
}

// 输出
void solve(void)
{
	printf("%d\n", dfs(0, W));
}

记忆化

我们用一组数据来看一下暴力搜索的过程。

n = 4, W = 5
(w, v) = {(2, 3), (1, 2), (3, 4), (2, 2)}

我们用二叉树的形式来描述该过程:
暴力搜索过程
我们发现 \(dfs(3, 2)\) 执行了两次,这显然造成了浪费。如果能把第一次的结果记录下来,那么就避免了第二次的计算。这就是记忆化搜索,把搜索过程中的结果记录下来,避免重复的搜索。

// 输入
int n, W;                    // n -- 物品个数;W -- 最大重量
int w[WMAX], v[VMAX];        // w[WMAX] -- 物品重量;v[VMAX] -- 物品价值
int dp[MAX][MAX];            // 记忆化数组,必须足够大

// dfs(i, j) -- 从编号为i开始挑选总重不超过j的物品,返回最大价值
int dfs(int i, int j)
{
	if (i == n)
		return 0;
	
	// 使用已经计算过的结果
	if (dp[i][j] >= 0)
		return dp[i][j];
	
	int ret;
	if (j < w[i])
		ret = dfs(i + 1, j);
	else
		ret = max(dfs(i + 1, j), dfs(i + 1, j - w[i]) + v[i]);
		
	return ret;
}

// 输出
void solve(void)
{
	memset(dp, -1, sizeof(dp));    // 初始化记忆数组
	
	printf("%d\n", dfs(0, W));
}

不难看出,与暴力搜索相比,记忆化搜索在原来的基础上,只不过多了 记忆化数组的声明和初始化访问记忆化数组
值得注意的是,记忆化数组必须开得足够大。因为它是用来记录 \(dfs(i,j)\) 的结果的,所以必须使 \(dp[i][j]\) 总是合法,不会越界访问


动态规划

对于动态规划来说,最重要的就是 递推关系式。一般,我们可以先写出搜索算法,再得到递推式;我们也可以直接得出递推式。

由搜索递归函数得到递归式

由上文 暴力搜索 提到的函数递归式,我们可以写出动态规划的递推式。

\[\begin{split} dp[n][j]&=0 \\ dp[i][j]&=\begin{cases} dp[i+1][j]&,j<w_i \\ max\{dp[i+1][j],dp[i+1][j-w_i]+v_i\}&,j\ge w_i \end{cases} \end{split} \]

有了递推式,我们就可以通过循环来计算。

// 输入
int n, W;                    // n -- 物品个数;W -- 最大重量
int w[WMAX], v[VMAX];        // w[WMAX] -- 物品重量;v[VMAX] -- 物品价值
int dp[MAX][MAX]             // dp数组,与记忆化数组一样,必须足够大

void solve(void)
{
	for (int i = n - 1; i >= 0; i--)
	{
		for (int j = 0; j <= W; j++)
		{
			if (j < w[i])
				dp[i][j] = dp[i + 1][j];
			else
				dp[i][j] = max(dp[i + 1][j], dp[i + 1][j - w[i]] + v[i]);
		}
	}

	printf("%d\n", dp[0][W]);
}

直接写出递归式

定义 \(dp[i][j]=\) 从前 \(i\) 个中选出总重量不超过 \(j\) 的物品。(因为编号从 \(0\) 开始,所以前 \(i+1\)个物品,最后一个物品的编号为 \(i\) )

\[\begin{split} dp[0][j]&=0 \\ dp[i + 1][j]&=\begin{cases} dp[i][j]&,j<w_i \\ max\{dp[i][j],dp[i][j-w_i]+v_i\}&,j\ge w_i \end{cases} \end{split} \]

// 输入
int n, W;                    // n -- 物品个数;W -- 最大重量
int w[WMAX], v[VMAX];        // w[WMAX] -- 物品重量;v[VMAX] -- 物品价值
int dp[MAX][MAX]             // dp数组,与记忆化数组一样,必须足够大

void solve(void)
{
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j <= W; j++)
		{
			if (j < w[i])
				dp[i + 1][j] = dp[i][j];
			else
				dp[i + 1][j] = max(dp[i][j], dp[i][j - w[i]] + v[i]);
		}
	}

	printf("%d\n", dp[n][W]);
}

标签:01,int,void,dfs,问题,--,背包,物品,dp
From: https://www.cnblogs.com/hoyd/p/18007597

相关文章

  • linux新安装系统后常遇到的问题
    没有如ll这种快捷命令vim/root/.bashrc后添加以下内容exportLS_OPTIONS='--color=auto'aliasls='ls$LS_OPTIONS'aliasll='ls$LS_OPTIONS-l'aliasl='ls$LS_OPTIONS-lA'aliasrm='rm-i'aliascp='cp-i'......
  • 2024年大数据面试的热门问题
    大数据是涉及以TB或PB为单位的大型数据集的大量数据。根据一项调查,今天大约90%的数据是在过去两年中产生的。大数据帮助公司对其提供的产品和服务产生有价值的见解。近年来,每家公司都使用大数据技术来完善其营销活动和技术。对于那些对准备跨国公司大数据面试感兴趣的人来说,本文是......
  • [Elasticsearch] Elasticsearch 启动访问报错问题
    Elasticsearch启动访问报错问题产生的问题与解决方案环境:Windows10ES版本:8.12.0现象:双击elasticsearch.bat文件启动后,访问http://127.0.0.1:9200地址报了一个错误:receivedplaintexthttptrafficonanhttpschannel,closingconnectionNetty4HttpChannel.........
  • 问题:决定价格的主要因素有哪些?
    问题:决定价格的主要因素有哪些?参考答案如图所示......
  • 问题:对壁细胞的功能有调节作用的细胞是
    问题:对壁细胞的功能有调节作用的细胞是A、S细胞B、G细胞C、ECL细胞D、D细胞E、I细胞参考答案如图所示......
  • 问题:在TCP的拥塞控制中,什么时候会使拥塞窗口重置为1?
    问题:在TCP的拥塞控制中,什么时候会使拥塞窗口重置为1?A:发生拥塞时;B:拥塞窗口超过慢开始门限时;C:分组超时时;D:慢开始门限重置为拥塞窗口的一半时参考答案如图所示......
  • 问题:【判断题】:云层越厚越暗,预示大雨即将来临。()
    问题:【判断题】:云层越厚越暗,预示大雨即将来临。()参考答案如图所示......
  • 问题:开链运动针对训练,鼓励,刺激训练目标肌肉
    问题:开链运动针对训练,鼓励,刺激训练目标肌肉参考答案如图所示......
  • mysql问题记录
    Mac下brew安装mysqlsudomysql.serverstart报错StartingMySQL.Loggingto'/usr/local/var/mysql/192.168.0.102.err'...ERROR!TheserverquitwithoutupdatingPIDfile(/usr/local/var/mysql/192.168.0.102.pid).解决办法sudochown-Rmysql/usr/local/var......
  • .NET周刊【1月第3期 2024-01-24】
    国内文章.NET开源的简单、快速、强大的前后端分离后台权限管理系统https://www.cnblogs.com/Can-daydayup/p/17980851本文介绍了中台Admin,一款基于Vue3和.NET8的开源后台权限管理系统。它具备前后端分离架构,支持多租户、接口和数据权限、动态Api等功能,并集成了多种中间件和服务......