很久没有接触动态规划了,通过b站正月点灯笼的视频重新学习了一遍:学习视频地址。总结了一下文字版方便复习:(因为最近都在学习JS,代码部分会用JS写一遍,算法核心不变)
Fibonacci Sequence斐波那契数列
用斐波那契数列理解动态规划问题:我们知道斐波那契数列有个递归公式fib(n) = fib(n-1) + fib(n-2)
。所以通过递归我们可以轻松的实现求fib(n)
JavaScript实现:
function fib(n) {
if (n == 1 || n == 2) return 1;
return fib(n-1) + fib(n-2);
}
但是这样实现会有一个问题,就是会出现重叠子问题,导致我们递归算法的时间复杂度比较高O(2^n)
。
什么是重叠子问题(overlap sub-problem)呢?我们看下面的树状分析图,当我们要计算fib(7)
的时候,fib(5)
、fib(4)
…等等都要计算两次,这就是重叠子问题。如果我们能将fib(i)
的值存储起来,避免重复计算,时间复杂度就能大大减少。
也就是说我们放弃递归,而是采取从前往后计算,如下图,时间复杂度就会变为O(n)
:
JavaScript代码实现:
function fib(n) {
var arr = [1,1];
if (n < 3) return arr[n - 1];
for (let i = 2; i < n; i++) {
arr[i] = arr[i-1] + arr[i-2];
}
return arr[n-1]
}
这第二种解法就是动态规划的雏形了。理解了斐波那契数列问题后,来看下面这个题目:
1.最佳选择问题One
Q:时间坐标系里有从上到下八个任务,它们的横坐标表示必须在[i,j]
时间段内完成,红色的数字表示完成该项目后能获得的报酬。问选择任意多个任务后能获得的最多报酬是多少?(同一时间只能做单一个项目)
通常动态规划的问题就是选和不选的问题,我们先写出递归公式:
- OPT(i):前i个任务可能获得的最高报酬
- v(i):第i个任务的报酬
- prev(i):如果选了第i个,前面最近能选的任务编号
JavaScript代码实现:
//递归算法:
var tasks = [5, 1, 8, 4, 6, 3, 2, 4];
var prev = [0, 0, 0