动态规划是一种非常重要的算法设计思想。历史上有很多著名的算法都是基于这种思想设计而来的,例如:Needleman–Wunsch算法、CYK算法、FFT算法、维特比算法等等。动态规划的核心思想有两个:首先是将一个大问题拆解为若干子问题;其次是将曾经计算过的结果储存起来以备多次使用。动态规划问题在现实中的变化是非常复杂的,要想尽述是不太可能的。因此,笔者也打算就此收尾。在本系列的最后一篇文章里,我们将通过一些例子(难度级别从Easy到Hard),谈谈如何构建从memoization到tabulation的动规解答(尽管之前我们也已经演示过类似的技术)。
例题A:canSum
这道题目前并不存在于LeetCode题库中,但它仍然是一道比较经典(而且也比较简单)的动规问题。给定一个字符串nums和一个整数target,问用nums中的数字自由加和,能否得到target。其中,每个数字可以反复使用多次,而且假定所有的数字都是非负的。
这个问题有点像之前讨论过的“#39组合和”,但彼时要求枚举出所有的组合,因此使用的方法是基于DFS的回溯法。基于之前的方法,写出一个DFS的版本并不难:
class Solution:
def canSum(self, nums: List[int], target: int) -> bool:
def dfs(s):
if s == 0: return True
if s