[学习记录][算法]动态规划
2015-04-29 11:30
573 查看
什么是动态规划?专业的解释:
动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。(看不懂……)
不懂,还是先看看实际中一些适用于动态规划的问题的特征吧。
在实际计算的过程中,我们会发现有些问题是可以用递推公式或者某些初始条件推导来获取答案的,这个问题本身依赖于它的多个子问题,而它的子问题又依赖于其他的子问题。用略数学的方法去描述就是,求一个关于n的问题,它可以由关于n-1、n-2或者n-1、n-2、n-3(或者更多低级别的问题)推倒出来。那么这样的问题就可以用动态规划来解决。
先举个运用动态规划解决的问题。
本题来自于leetcode:
https://leetcode.com/problems/climbing-stairs/
You are climbing a stair case. It takes n steps to reach to the top.
Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?
题目大意:你在爬一个n阶的台阶,每次可以爬一个台阶,或者两个,你有多少种爬到台阶顶部的不同方案。
首先想到的方法,一定是暴力算法,把爬n各台阶的每一种攀登方式一一写出来,费时费力,但是这其实是不必要的。
运用动态规划的方法,首先去分析子问题间的依存关系是怎样的,也就是把它当做一个数学问题,想出它的递推公式。在爬上第n阶前的位置,爬台阶的人一定处于n-1阶或n-2阶,并且他在n-1阶时向上走一个台阶走到n阶,或者在n-2阶时向上走两个台阶走上n阶,即这个n阶台阶的问题和两个子问题有关,n-1阶台阶问题和n-2阶台阶问题。用W(n)表示爬n个台阶的方法,那么有W(n)=W(n-1)+W(n-2)。
获得了它的子问题间的关系以后,会有两种基本的解题方向:
1.自顶向下,即采用从n向下递推的方式。求n时,需要求n-1和n-2,那么就分别求n-1和n-2,采用伪代码去实现它就是:
climbStairs(n){
if(n==1){
return 1;}else if(n==2){
return 2;
}
return climbStairs(n-1)+climbStairs(n-2);
}
这个方案我们可以看到,它的时间复杂度为O(n^2)。
我们可以发现,其实在求 climbStairs(n-1)时,我们就已经求过了climbStairs(n-2),而在之后的climbStairs(n-2)中我们又再次求了climbStairs(n-2)的结果,这造成了一定的时间浪费,我们可以采用以个记忆数组,去标记第i阶问题的答案,当发现这个数组中i对应的位置已经有了结果,就可以不必计算,直接取用。
采用伪代码去实现它就是:
climbStairs_mem(n,mem[]){
if(n==1){
return 1;}else if(n==2){
return 2;
}
if(mem
!=0){
mem
= climbStairs(n-1, mem[])+climbStairs(n-2, mem[]);
}
return mem
;
}
2.采用自底向上的方法,从只有一个台阶、两个台阶,逐个向上递推。
代码如下:
public int climbStairs(int n) {
if(n==1){
return 1;
}else if(n==2){
return 2;
}
int step=3;
int x=1;
int y=2;
while(step<=n){
int temp = x+y;
x=y;
y=temp;
step++;
}
return y;
}
在O(n)的时间内解出答案。而且只需要占用两个额外的空间。
根据以上的题,来总结一下动态规划的特点。
1. 问题的最优解包含其子问题的最优解。如上,攀登n阶台阶的最多的方法包含攀登n-1阶台阶的最多的方法、n-2阶台阶的的最多的方法。
2. 重叠子问题。即对于攀登n阶台阶的方法,会划分为计算攀登n-1和1级台阶的方案、攀登n-2和2级台阶的方案,攀登n-3和3级台阶的方案,但是,我们会发现,在求解n-2阶台阶攀登方法时,这个问题是可以划分为攀登n-3和1级台阶的方案及其他,那么,这个n-3级台阶的方案,我们可能在实际求解时会计算多次,这个子问题是重叠的。将这个重叠的子问题由计算多次变为计算一次,可以大大降低时间复杂度。
通过比较上题两个解题方法,我们也可以发现,动态规划问题采用自底向上的算法要比自顶向上的算法时间复杂度小,在可能的情况下,可以优先选择自底向上。
动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。(看不懂……)
不懂,还是先看看实际中一些适用于动态规划的问题的特征吧。
在实际计算的过程中,我们会发现有些问题是可以用递推公式或者某些初始条件推导来获取答案的,这个问题本身依赖于它的多个子问题,而它的子问题又依赖于其他的子问题。用略数学的方法去描述就是,求一个关于n的问题,它可以由关于n-1、n-2或者n-1、n-2、n-3(或者更多低级别的问题)推倒出来。那么这样的问题就可以用动态规划来解决。
先举个运用动态规划解决的问题。
本题来自于leetcode:
https://leetcode.com/problems/climbing-stairs/
You are climbing a stair case. It takes n steps to reach to the top.
Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?
题目大意:你在爬一个n阶的台阶,每次可以爬一个台阶,或者两个,你有多少种爬到台阶顶部的不同方案。
首先想到的方法,一定是暴力算法,把爬n各台阶的每一种攀登方式一一写出来,费时费力,但是这其实是不必要的。
运用动态规划的方法,首先去分析子问题间的依存关系是怎样的,也就是把它当做一个数学问题,想出它的递推公式。在爬上第n阶前的位置,爬台阶的人一定处于n-1阶或n-2阶,并且他在n-1阶时向上走一个台阶走到n阶,或者在n-2阶时向上走两个台阶走上n阶,即这个n阶台阶的问题和两个子问题有关,n-1阶台阶问题和n-2阶台阶问题。用W(n)表示爬n个台阶的方法,那么有W(n)=W(n-1)+W(n-2)。
获得了它的子问题间的关系以后,会有两种基本的解题方向:
1.自顶向下,即采用从n向下递推的方式。求n时,需要求n-1和n-2,那么就分别求n-1和n-2,采用伪代码去实现它就是:
climbStairs(n){
if(n==1){
return 1;}else if(n==2){
return 2;
}
return climbStairs(n-1)+climbStairs(n-2);
}
这个方案我们可以看到,它的时间复杂度为O(n^2)。
我们可以发现,其实在求 climbStairs(n-1)时,我们就已经求过了climbStairs(n-2),而在之后的climbStairs(n-2)中我们又再次求了climbStairs(n-2)的结果,这造成了一定的时间浪费,我们可以采用以个记忆数组,去标记第i阶问题的答案,当发现这个数组中i对应的位置已经有了结果,就可以不必计算,直接取用。
采用伪代码去实现它就是:
climbStairs_mem(n,mem[]){
if(n==1){
return 1;}else if(n==2){
return 2;
}
if(mem
!=0){
mem
= climbStairs(n-1, mem[])+climbStairs(n-2, mem[]);
}
return mem
;
}
2.采用自底向上的方法,从只有一个台阶、两个台阶,逐个向上递推。
代码如下:
public int climbStairs(int n) {
if(n==1){
return 1;
}else if(n==2){
return 2;
}
int step=3;
int x=1;
int y=2;
while(step<=n){
int temp = x+y;
x=y;
y=temp;
step++;
}
return y;
}
在O(n)的时间内解出答案。而且只需要占用两个额外的空间。
根据以上的题,来总结一下动态规划的特点。
1. 问题的最优解包含其子问题的最优解。如上,攀登n阶台阶的最多的方法包含攀登n-1阶台阶的最多的方法、n-2阶台阶的的最多的方法。
2. 重叠子问题。即对于攀登n阶台阶的方法,会划分为计算攀登n-1和1级台阶的方案、攀登n-2和2级台阶的方案,攀登n-3和3级台阶的方案,但是,我们会发现,在求解n-2阶台阶攀登方法时,这个问题是可以划分为攀登n-3和1级台阶的方案及其他,那么,这个n-3级台阶的方案,我们可能在实际求解时会计算多次,这个子问题是重叠的。将这个重叠的子问题由计算多次变为计算一次,可以大大降低时间复杂度。
通过比较上题两个解题方法,我们也可以发现,动态规划问题采用自底向上的算法要比自顶向上的算法时间复杂度小,在可能的情况下,可以优先选择自底向上。
相关文章推荐
- 【算法学习】双调欧几里得旅行商问题(动态规划)
- 算法学习记录 N皇后问题 递归解法
- 【算法学习】最优二叉查找树(动态规划)
- 【算法学习】最优二叉查找树(动态规划)
- 算法学习-动态规划-实战一
- STL学习记录(十四):其它几种算法
- python 算法学习部分代码记录篇章1
- MCMC与GIBBS算法学习记录
- 算法学习--动态规划
- 算法学习记录-排序——归并排序
- 蒜头学算术,计蒜客算法学习记录
- 算法设计技巧和分析学习笔记1 (归纳法、分治和动态规划)
- 算法学习笔记----用动态规划解决钢管切割问题
- 【学习记录】5.2 数组相关的基本算法
- 【算法导论学习-29】动态规划经典问题02:最长公共子序列问题(Longest common subsequence,LCS)
- 【笔记】【算法学习】【动态规划】背包问题总结(1)
- 基础算法学习笔记(二)----动态规划
- 【算法学习】最优二叉查找树(动态规划)
- 算法学习记录十一(C++)--->调整数组顺序使奇数前偶数后
- 【算法学习】最优二叉查找树(动态规划)