1 动态规划和分治的区别
分治:问题分成几个独立的子问题,他们会分别解决。最后再将各个子问题的结果合并成一个大的结果
动态规划:问题分成几个相互依赖或者重叠的问题。使用空间换时间的方法避免重复计算,一般从下往上计算子问题。
1.1 动态规划使用条件:优化原则
2 动态规划举例:斐波那契数列
2.1 斐波那契额数列的描述
其中F(0)=0,F(1)=1
2.2 斐波那契数列的通项
高中还会求的,好像是解一元二次方程然后算系数(记不清了,之后回家翻一下笔记补上去。。。)
总之,斐波那契数列的通项是:
其中是黄金比例
与此同时,我们有:
2.3 通过定义迭代计算
2.3.1 时间复杂度分析
最终我们能得到:
时间复杂度是指数级的,这么高的原因是:重复计算
我们计算F(n)的时候,像F(n-2)这种就计算了两边,越往下,重复的越多
2.4 动态规划思路
吸取上面的教训,如果我们将记录 保存下来呢?
这个的时间复杂度是O(n) 【每个Fib(k)都需要算一次,用一次加法两次索引即可】
2.4.1 此时是多项式复杂度吗?
看起来是,实际上不是。
我们这样想:
这里的输入是一个数字n,用二进制表示,是需要用位来表示;用十进制表示,也是logn位了。
但是我们所需要的时间复杂度是O(n),相对于输入的O(logn)数量级来说,是数量级了。
所以可以说,此时的时间复杂度是
3 最长公共子序列
3.1 暴力枚举法
一个一个子序列比较
此时的时间复杂度是(这里2的指数是m或者n都可以【取小的那个】,就是指数数量级)
我们有个不同的子序列(每个元素可以取可以不取,两个可能性)
对于每个子序列,我们需要做O(n+m)的比对工作
解释一下O(n+m),假如n<m。
我们任取一个子序列X(X的长度小于等于n)
首先比对X与长度位m中的序列的元素,假如长度位m的序列一共遍历到下长方形标蓝的位置,找到X中的元素。
那么下一个元素(黄),直接从标蓝的后一位开始,不用倒退回找。
所以最差的情况,是长度为n和长度为m的序列都遍历完了。
所以时间复杂度是O(n+m)
3.2 动态规划
3.1 探索性思路
注:从前往后一路下去也是可以的
3.2 动态规划算法
3.3 求LCS
4 带负边最短路径问题
如果边权重有负数,那么此时之前的迪杰斯特拉算法还生效吗?
答案是:不生效
考虑上图,如果我们用迪杰斯特拉算法的话,一开始是先从左边的0到1,再从1到中间的0 ,最后从中间的0到3.
但是实际上最短路径是左0->5->中0->3
4.1 负环
负循环是一个有向循环,其边长之和为负。
有负循环的图肯定没有最短路径(我们绕着负循环一圈一圈绕,路径的长度就会不断地降下来)
4.1.1 负环的几个定理
1)如果从s到t的路径中有负环,那么不存在一条从s到t的最短路径
2)如果G没有负环,那么一定存在一条从s到t的最短路径
4.2 解决方法:动态规划
令d(v,k)表示从s到v的、最多有k条边的最短路径
于是我们最终要求的结果是:【最多n-1条边,就是每个节点最多遍历一次】
那么d(v,k)怎么计算呢?
- 如果最短路径的边数量≤k-1,那么d(v,k)=d(v,k-1)
- 如果最短路径有k条边,我们令(u,v)是最后的一条边,那我们就是寻找s到u的最短路径,其中最多有k-1条边
起始数据信息:
起始点d(s,0)=0
其他的点v:d(v,0)=∞
4.3 Bellman-Ford 算法
4.3.1 时间复杂度分析
4.3.2 空间复杂度分析
记录每条边的长度:O(m)
所有的d(u,k):O(n^2)
所以空间复杂度是O(m+n^2)
4.3.3 空间复杂度改良算法
每一个k的d(u,k)只会使用一次,就是d(u,k+1)的时候,因此,不用每层都保存下来
时间复杂度不变
空间复杂度降至O(m+n)
4.4 寻找负循环
证明:
4.5 寻找负循环
4.6 all-pair shortest path
5 矩阵相乘
5.1 题目描述
5.2.解题思路
5.2.1 暴力枚举
5.2.2 动态规划
5.2.3 递归实现动态规划
5.2.4 迭代实现
即使用memory记录
换言之,i从0~n,j从i+1~n,k从i~j,三个都需要循环,所以时间复杂度是O(n3)
5.2.5 两种算法的比较
6 动态规划算法设计步骤
7举例 :MaxSum
7.1 题目描述
7.2 方法1:枚举
7.3 方法2:分治
7.4 方法3:动态规划