动态规划之背包问题
1. 动态规划(Dynamic Programming, DP)问题
1.1 基本思想
动态规划背后的基本思想非常简单。大致上,若要解一个给定问题,我们需要解其不同部分(即子问题),再根据子问题的解以得出原问题的解。
通常许多子问题非常相似,为此动态规划法试图仅仅解决每个子问题一次,从而减少计算量:一旦某个给定子问题的解已经算出,则将其记忆化存储,以便下次需要同一个子问题解之时直接查表。这种做法在重复子问题的数目关于输入的规模呈指数增长时特别有用。
所以,动态规划问题是一种用空间换时间的算法。
1.2 适用情况
- 最优子结构性质:
如果问题的最优解所包含的子问题的解也是最优的,我们就称该问题具有最优子结构性质(即满足最优化原理)。最优子结构性质为动态规划算法解决问题提供了重要线索。
关于最优子结构的例子,参考【动态规划】01背包问题,例子很清楚。
- 无后效性:
即子问题的解一旦确定,就不再改变,不受在这之后、包含它的更大的问题的求解决策影响。
同样地,无后效性例子参考【动态规划】01背包问题。
- 子问题重叠性质。
指在用递归算法自顶向下对问题进行求解时,每次产生的子问题并不总是新问题,有些子问题会被重复计算多次。动态规划算法正是利用了这种子问题的重叠性质,对每一个子问题只计算一次,然后将其计算结果保存在一个表格中,当再次需要计算已经计算过的子问题时,只是在表格中简单地查看一下结果,从而获得较高的效率。
1.3 DP Vs. 分治法
分治法(divide-and-conquer)是将原问题划分成一些独立的子问题,递归地求解各子问题,然后合并子问题的解得到原问题的解,比如归并排序(Merge Sort)就是一种典型的分治法。
与此不同,动态规划适用于子问题独立且重叠的情况,也就是各子问题包含公共的子子问题。在这种情况下,若用分治法则会做许多不必要的工作,即重复地求解公共的子问题。动态规划算法对每个子子问题只求解一次,将其结果保存在一张表中,从而避免每次遇到各个子问题时重新计算答案。
算法导论:动态规划要求其子问题既要独立又要重叠,这看上去似乎有些奇怪。虽然这两点要求听起来可能矛盾的,但它们描述了两种不同的概念,而不是同一个问题的两个方面。如果同一个问题的两个子问题不共享资源,则它们就是独立的。对两个子问题俩说,如果它们确实是相同的子问题,只是作为不同问题的子问题出现的话,是重叠的,则它们是重叠的。
1.4 DP Vs. 贪心算法
贪心算法是使所做的选择看起来都是当前最佳的,期望通过所做的局部最优选择来产生出一个全局最优解。贪心算法对大多数优化问题来说能产生最优解,但也不一定总是这样的。
贪心算法与动态规划有一个显著的区别,就是贪心算法中,是以自顶向下的方式使用最优子结构的。贪心算法会先做选择,在当时看起来是最优的选择,然后再求解一个结果子问题,而不是先寻找子问题的最优解,然后再做选择。
标准分治 | 动态规划 | 贪心算法 | |
---|---|---|---|
适用类型 | 通用问题 | 优化问题 | 优化问题 |
子问题结构 | 每个子问题不同 | 很多子问题重复(不独立) | 只有一个子问题 |
最优子结构 | 不需要 | 必须满足 | 必须满足 |
子问题数 | 全部子问题都需解决 | 全部子问题都需解决 | 只需解决一个子问题 |
子问题在最优解里 | 全部 | 部分 | 部分 |
选择与求解顺序 | 先选择后解决子问题 | 先解决子问题后选择 | 先选择后解决子问题 |
2. 0-1背包问题
2.1 问题描述
一个背包的容量为V,现有N种物品,第i个物品的重量为weight[i],价值为value[i].现在往背包里装东西,怎样装才能使价值最大。
2.2 问题分析
法 |
||
3. 完全背包问题
// TODO
4. 多重背包问题
// TODO