LintCode 背包问题

本文以通俗易懂的方式介绍01背包问题及其动态规划解法,通过实例详细解析状态转移方程,帮助读者掌握核心算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背包问题 
 

注意事项

你不可以将物品进行切割。

样例

如果有4个物品[2, 3, 5, 7]

如果背包的大小为11,可以选择[2, 3, 5]装入背包,最多可以装满10的空间。

如果背包的大小为12,可以选择[2, 3, 7]装入背包,最多可以装满12的空间。

函数需要返回最多能装满的空间大小。



首先给一个背包问题通俗易懂的讲解,这里使用的是从杰大佬的一个很形象的讲解,附上他的博客链接:

01背包问题,是用来介绍动态规划算法最经典的例子,网上关于01背包问题的讲解也很多,我写这篇文章力争做到用最简单的方式,最少的公式把01背包问题讲解透彻。

01背包的状态转换方程 f[i,j] = Max{ f[i-1,j-Wi]+Pi( j >= Wi ),  f[i-1,j] }

f[i,j]表示在前i件物品中选择若干件放在承重为 j 的背包中,可以取得的最大价值。
Pi表示第i件物品的价值。
决策:为了背包中物品总价值最大化,第 i件物品应该放入背包中吗 ?

题目描述:

有编号分别为a,b,c,d,e的五件物品,它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?

nameweightvalue12345678910
a26066991212151515
b23033669991011
c65000666661011
d54000666661010
e460006666666

只要你能通过找规律手工填写出上面这张表就算理解了01背包的动态规划算法。

首先要明确这张表是至底向上,从左到右生成的。

为了叙述方便,用e2单元格表示e行2列的单元格,这个单元格的意义是用来表示只有物品e时,有个承重为2的背包,那么这个背包的最大价值是0,因为e物品的重量是4,背包装不了。

对于d2单元格,表示只有物品e,d时,承重为2的背包,所能装入的最大价值,仍然是0,因为物品e,d都不是这个背包能装的。

同理,c2=0,b2=3,a2=6。

对于承重为8的背包,a8=15,是怎么得出的呢?

根据01背包的状态转换方程,需要考察两个值,

一个是f[i-1,j],对于这个例子来说就是b8的值9,另一个是f[i-1,j-Wi]+Pi;

在这里,

 f[i-1,j]表示我有一个承重为8的背包,当只有物品b,c,d,e四件可选时,这个背包能装入的最大价值

f[i-1,j-Wi]表示我有一个承重为6的背包(等于当前背包承重减去物品a的重量),当只有物品b,c,d,e四件可选时,这个背包能装入的最大价值

f[i-1,j-Wi]就是指单元格b6,值为9,Pi指的是a物品的价值,即6

由于f[i-1,j-Wi]+Pi = 9 + 6 = 15 大于f[i-1,j] = 9,所以物品a应该放入承重为8的背包



-------------------------------------------------------------华丽的分割线----------------------------------------------------------------------------------------------

本题由于没有物品价值这个维度,但是可以直接把物品的重量当做物品的价值,最后依然是求解出背包中最大价值的问题。

上面的解释中数组的更新是从底向上不是很直观,我们从上到下其实是一样的道理,二维数组result[A.size()][m+1] 中的result[i][j]表示在背包空间为j,有A中前i个物品可选的情况下,最大的价值是什么。

f[i,j] = Max{ f[i-1,j-Wi]+Pi( j >= Wi ),  f[i-1,j] }


其中   value1= f[i-1,j]  意味着第i个物品不放到背包中,所以背包中的最大价值还是和i-1个候选物品时一样
而 value2=   f[i-1,j-Wi]+Pi( j >= Wi) 表示我的背包如果一定要放入第i个物品的话,那么我先给i号物品预留wi的空间,所以背包的总价值是j-wi的空间情况下i-1个候选物品的最大价值+第i个物品的价值。

通过比较value1和value2的值,我就可以做出决策来让在空间为j,候选物品为前i个,的情况下的最大价值。

所以我们可以得到我们的程序如下:
但是这个程序在大数据量的时候会爆内存,所以我们需要继续优化我们的代码↓
int backPack(int m, vector<int> A) {
	// write your code here
	if (A.size() == 0)
		return 0;
	if (m == 0)
		return 0;

	vector<vector<int>> result(A.size(), vector<int>(m + 1, 0));

	for (int i = 0; i <= m; i++)
	{
		if (i >= A[0])
			result[0][i] = A[0];
	}

	for (int i = 1; i < A.size(); i++)
	{
		for (int j = 0; j <= m; j++)
		{
			if (j - A[i] >= 0)
				result[i][j] = max(result[i - 1][j], result[i- 1][j - A[i]] + A[i]);
			else
				result[i][j] = result[i- 1][j];
		}
	}
	return result[A.size()][m];
}


同时通过观察状态转移方程我们可以看到,每一行的计算其实只会用到上一行的数据,所以其实我们只需要一个2行,m+1列的数组就能够完成计算了,所以这里需要一些小的技巧,利用i的奇偶特性来确定当前使用哪一行来保存数据,具体的代码如下:

int backPack(int m, vector<int> A) {
	// write your code here
	if (A.size() == 0)
		return 0;
	if (m == 0)
		return 0;

	vector<vector<int>> result(2, vector<int>(m + 1, 0)); 
	//本来应该用二维数组存储,但是其实每一次计算只会用到上一行的数据,所以可以直接压缩到一个两行的数组

	for (int i = 0; i <= m; i++)
	{
		if (i >= A[0])
			result[0][i] = A[0];
	}

	for (int i = 1; i < A.size(); i++)
	{
		for (int j = 0; j <= m; j++)
		{
			if (j - A[i] >= 0)
				result[i % 2][j] = max(result[abs(i % 2 - 1)][j], result[abs(i % 2 - 1)][j - A[i]] + A[i]);
			else
				result[i % 2][j] = result[abs(i % 2 - 1)][j];
		}
	}
	return result[A.size() % 2 == 0 ? 1 : 0][m];
}



### LintCode 1331 题目解析 LintCode 平台上编号为 1331 的题目尚未被明确提及于所提供的引用中。然而,基于平台上的常见问题类型以及相关算法设计模式[^4],可以推测该类题目通常涉及深度优先搜索(DFS)、动态规划或其他高级算法。 #### 假设场景下的 DFS 实现 如果假设此题与图遍历或组合生成有关,则可采用如下模板实现: ```python def dfs(data, result, path, index): if index == len(data): # 边界条件判断 result.append(list(path)) # 添加当前路径到结果集中 return for i in range(len(data[index])): path.append(data[index][i]) # 执行动作 dfs(data, result, path, index + 1) # 递归调用 path.pop() # 恢复状态 # 调用示例 data = [[1, 2], [3, 4]] result = [] dfs(data, result, [], 0) print(result) ``` 上述代码展示了如何通过递归方式构建所有可能的排列组合,并将其存储至 `result` 列表中。 #### 动态规划思路分析 对于某些优化问题,例如背包问题变种,可能会应用类似于以下的状态转移方程[^3]: \[ \text{dp}[i][j] = \max(\text{dp}[i-1][j-A[i-1]] + A[i-1], \text{dp}[i-1][j]) \] 其中 \(A\) 表示物品价值数组,而 \(j\) 是容量变量。这种形式能够有效解决资源分配中的最大化收益问题。 #### 数字统计逻辑探讨 当遇到需要计算特定范围内某数位出现次数的情况时,类似下面的方法会被采纳[^2]: ```python def digit_counts(k, n): count = 0 for num in range(n+1): str_num = str(num) if k == 0 and num == 0: count += 1 elif str(k) in str_num: count += str_num.count(str(k)) return count ``` 这里定义了一个函数用于统计整数区间 `[0,n]` 中数字 `k` 出现总频次。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值