何为动态规划?
动态规划算法是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推的方式去解决。“大而化小,小而化无”。
什么时候用动态规划?
能采用动态规划求解的问题的一般要具有3个性质:
(1)最优化原理:假设问题的最优解所包括的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。
(2)无后效性:即某阶段状态一旦确定。就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响曾经的状态。仅仅与当前状态有关;
(3)有重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到(该性质并非动态规划适用的必要条件,可是假设没有这条性质。动态规划算法同其它算法相比就不具备优势)。
怎么使用动态规划?
-
利用动态规划思想从上往下思考问题:将多阶段问题转变成更小的多阶段问题(状态转移方程)
-
分解至最小的单阶段问题(可直接解决问题)。
-
利用循环从下往上解决问题。
下面是一道简单的动态规划题,通过这道题讲解,了解如何使用动态规划。
阶梯问题
有N阶台阶,每一步可以走1步台阶或者2步台阶,求出走到第N阶台阶的方法数。
-
多阶段问题转变成更小的多阶段问题
假设我们现在还有最后一步要走,可能的情况有哪些?
1)我们站在第N-1级上,一步1级后到达顶端;
2)我们站在第N-2级上,一步2级后到达顶端;
所以,最后一步可以走1级或者2级,不外乎两种情况。因此有状态转移方程:
f(N)=f(N-1)+f(N-2)
-
可直接解决问题
f(1)=1 f(2)=2
-
下往上解决问题
f(1)=1 f(2)=2 f(3)=f(2)+f(1) ... ... f(N)=f(N-1)+f(N-2)
递归解决(自顶向下)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eDn7QAzG-1569663083177)(E:\F\blog\动态规划.PNG)]
public int f(int n){ if (n <1) { return 0; } if (n == 1){ return 1; } if (n == 2){ return 2; } return f(n-1) + f(n-2); }
从图中我们可以看出,没错,这是一棵二叉树,如图所示,这里同样颜色的表示重复计算的节点,而且多数都不止重复计算了一次。因此可以继续优化。
备忘录算法–递归优化
public int f(int n, Map<Integer,Integer> map){ if (n <1) { return 0; } if (n == 1){ return 1; } if (n == 2){ return 2; } if(map.containsKey(n)){ return map.get(n); }else{ int value = f(n-1,map) + f(n-2,map); map.put(n,value); return value; } }
动态规划(自底向上)
public int f(int n){ if (n <1) { return 0; } if (n == 1){ return 1; } if (n == 2){ return 2; } int a = 1; int b = 2; int temp = 0; for (int i = 3; i < n+1 ; i++) { temp = a + b; a = b; b= temp; } return temp; }
总结
动态规划是一种解决复杂问题的方法,它将大问题分成小问题或者说子问题,这些子问题是独立的,且会有重叠,也就是各子问题包含公共的子子问题,动态规划不会重复地求解公共子问题,而是对每个子问题只求解一次,将结果保存起来,从而避免每次遇到各个子问题时重新计算结果。适合采用动态规划方法的最优化问题中的两个要素:最优子结构和重叠子问题。
的方法,它将大问题分成小问题或者说子问题,这些子问题是独立的,且会有重叠,也就是各子问题包含公共的子子问题,动态规划不会重复地求解公共子问题,而是对每个子问题只求解一次,将结果保存起来,从而避免每次遇到各个子问题时重新计算结果。适合采用动态规划方法的最优化问题中的两个要素:最优子结构和重叠子问题。