如果想了解更多内容,欢迎关注我的微信公众号:信息学竞赛从入门到巅峰。
动态规划(简称DP)在当今赛场上所占的比例越来越多。今天,我们就来讲讲动态规划的入门问题——背包问题。
背包问题有众多类型,其中“01背包”、“完全背包”最为典型(当然,其他类型也很重要)。我们先来讲解一下如何解决“01背包”的问题。
问题化简
01背包的问题一般可以简化为:
有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使总价值最大。
一个错误的想法
看到这个问题,你的第一想法是不是像购物一样,挑性价比最高的放到背包中呢?然而,这是一种贪心(此处无贬义)的想法,存在严重的局限性的。
举个栗子,如果挑选性价比高的物品,可能消耗了较小的背包容量,但是背包剩下的容量也无法放下其他物品,导致了总价值无法到达最大。
下面这个表格清晰的体现了贪心算法的局限性。
假设背包容量V = 9, 物品数量N = 4。
| c[i](费用) | 7 | 3 | 3 | 3 |
| w[i](价值) | 5 | 2 | 2 | 2 |
显然,物品1的性价比最高,但是选择了物品1就无法再放其他物品。然而,选择物品2,3,4的价值为9,方案更优。
正解驾到
那么正解是什么呢?
我们先来讲解一下动态规划的核心思想:从局部最优解计算全局最优解。
换句话说,就是将原问题分解成若干个容易求解的小问题,通过小问题的答案来计算原问题的答案。

我们定义一个二位数组f[N][V]。
其中,f[i][j]表示前i件物品中,占用了背包j的容量,所能得到的最大价值。
当我们再考虑当前已经背包用了j的容量,第i+1件物品选不选的时候,显然f[1...i][0...V]已经都求出来了。
如果第i+1件物品不选,那么所得价值和f[i][j]的值是一样的。
如果第i+1件物品要选,那么前i件物品只能占用j-c[i+1]的背包容量。所以,这种情况下所得到的价值为f[i][j-c[i+1]] + w[i]。
综合上述两种情况,我们可以得到转移方程:
f[i][j] = max(f[i - 1][j], f[i - 1][j - c[i]] + w[i])
通过这个转移方程,我们很容易就能写出程序:
for (int i = 1; i <= N; ++i)
for (int j = 0; j <= V; ++j) //Attention: j start from 0 !!!
if (j >= c[i]) //important!!!
f[i][j] = max(f[i - 1][j], f[i - 1][j - c[i]] + w[i]);
int ans = 0;
for (int j = 0; j <= V; ++j)
ans = max(ans, f[N][j]); //find the biggest value
我们可以发现,01背包的时间复杂度是O(NV),空间复杂度也是O(NV)。
但是,01背包的空间复杂度是可以优化的,具体请关注我的公众号哦。
本文深入浅出地解析了动态规划中的经典问题——01背包问题,通过对比贪心算法的局限性,详细阐述了如何利用动态规划求解最大价值组合。
574

被折叠的 条评论
为什么被折叠?



