动态规划——递归DP算法(Dynamic Programing)
DP算法思想
- 将待求解的问题分解称若干个子问题,并存储子问题的解而避免计算重复的子问题,并由子问题的解得到原问题的解。
- 动态规划算法通常用于求解具有某种最有性质的问题。
- 动态规划算法的基本要素:最优子结构性质和重叠子问题。
- 最优子结构性质:问题的最优解包含着它的子问题的最优解。即不管前面的策略如何,此后的决策必须是基于当前状态(由上一次的决策产生)的最优决策。
- 重叠子问题:在用递归算法自顶向下解问题时,每次产生的子问题并不总是新问题,有些问题被反复计算多次。对每个子问题只解一次,然后将其解保存起来,以后再遇到同样的问题时就可以直接引用,不必重新求解。
DP算法——解决问题的基本特征
(1)动态规划一般求解最值(最优、最大、最小、最长)问题;
(2)动态规划解决 的问题一般是离散的,可以分解的(划分阶段的)。
(3)动态规划结局的问题必须包含最优子结构,即可以有(n-1)的最优推导出n的最优。
DP算法——解决问题的基本步骤
动态规划算法的四个步骤:
- 刻画最优解的结构特性。(一维、二维、三维数组);
- 递归的定义最优解。(状态转移方程)
- 以自底向上的方法来计算最优解。
- 从计算得到的解来构造一个最优解。
动态规划——DP算法(Dynamic Programing)
递归
递归:“归”的过程是产生答案的过程,“递”的过程是将大问题分解成子问题的过程。
“归”自底向上,“递”自顶向下。
递归搜索树的底就是最小子问题的答案。
BFS广度优先搜索
模板:
BFS 使用队列,把每个还没有搜索到的点依次放入队列,然后再弹出队列的头部元素当做当前遍历点。BFS 总共有两个模板:
如果不需要确定当前遍历到了哪一层,BFS 模板如下。
while queue 不空:
cur = queue.pop()
for 节点 in cur的所有相邻节点:
if 该节点有效且未访问过:
queue.push(该节点)
如果要确定当前遍历到了哪一层,BFS 模板如下。 这里增加了 level 表示当前遍历到二叉树中的哪一层了,也可以理解为在一个图中,现在已经走了多少步了。size 表示在当前遍历层有多少个元素,也就是队列中的元素数,我们把这些元素一次性遍历完,即把当前层的所有元素都向外走了一步。
level = 0
while queue 不空:
size = queue.size()
while (size --) {
cur = queue.pop()
for 节点 in cur的所有相邻节点:
if 该节点有效且未被访问过:
queue.push(该节点)
}
level ++;
回溯
回溯法:一种通过探索所有可能的候选解来找出所有的解的算法。如果候选解被确认不是一个解(或者至少不是最后一个解),回溯算法会通过在上一步进行一些变化抛弃该解,即回溯并且再次尝试。
「回溯算法」与「深度优先遍历」都有「不撞南墙不回头」的意思。「回溯算法」强调了「深度优先遍历」思想的用途,用一个 不断变化 的变量,在尝试各种可能的过程中,搜索需要的结果。强调了 回退 操作对于搜索的合理性。而「深度优先遍历」强调一种遍历的思想,与之对应的遍历思想是「广度优先遍历」。
回溯算法用于 搜索一个问题的所有的解 ,通过深度优先遍历的思想实现。
与动态规划的区别:
共同点:
用于求解多阶段决策问题。多阶段决策问题即:求解一个问题分为很多步骤(阶段);每一个步骤(阶段)可以有多种选择。
不同点:
动态规划只需要求我们评估最优解是多少,最优解对应的具体解是什么并不要求。因此很适合应用于评估一个方案的效果;回溯算法可以搜索得到所有的方案(当然包括最优解),但是本质上它是一种遍历算法,时间复杂度很高。
少,最优解对应的具体解是什么并不要求。因此很适合应用于评估一个方案的效果;回溯算法可以搜索得到所有的方案(当然包括最优解),但是本质上它是一种遍历算法,时间复杂度很高。