动态规划
最近在看牛客网的校招题目,发现很多公司的编程题都考了动态规划里面的知识,所以,专门抽个时间来学习一下。
首先,要用动态规划算法,得满足以下几个条件:
1.最优化原理(最优子结构性质) 最优化原理可这样阐述:一个最优化策略具有这样的性质,不论过去状态和决策如何,对前面的决策所形成的状态而言,余下的诸决策必须构成最优策略。简而言之,一个最优化策略的子策略总是最优的。一个问题满足最优化原理又称其具有最优子结构性质。
2.无后效性将各阶段按照一定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策,而只能通过当前的这个状态。换句话说,每个状态都是过去历史的一个完整总结。这就是无后向性,又称为无后效性。
3.子问题的重叠性 动态规划将原来具有指数级时间复杂度的搜索算法改进成了具有多项式时间复杂度的算法。其中的关键在于解决冗余,这是动态规划算法的根本目的。动态规划实质上是一种以空间换时间的技术,它在实现的过程中,不得不存储产生过程中的各种状态,所以它的空间复杂度要大于其它的算法。
动态规划的思想是什么:
记忆,空间换时间,不重复求解,由交叠子问题从较小问题解逐步决策,构造较大问题的解。
一般来说,一个经典的动态规划算法时自底向上的(从较小问题的解,由交叠性质,逐步决策处较大问题的解),它需要解出给定问题的所有较小子问题。动态规划的一个变种是试图避免对不必要的子问题求解。如果采用自顶向下的递归来解,那么就避免了不必要子问题的求解(相对于动态规划表现出优势),然而递归又会导致对
同一个子问题多次求解(相对于动态规划表现出劣势),所以将递归和动态规划结合起来,就可以设计一种基于记忆功能的从顶向下的动态规划算法,在后面会讲。
同一个子问题多次求解(相对于动态规划表现出劣势),所以将递归和动态规划结合起来,就可以设计一种基于记忆功能的从顶向下的动态规划算法,在后面会讲。
计算二项式系数:
在排列组合里面,我们有下面的式子:
这个式子将C(n , k)的计算问题表述为了(问题描述)C(n-1 , k -1)和C(n -1, k)两个较小的交叠子问题。
初始条件:C(n , n) = C(n , 0) = 1
我们可以用下列填矩阵的方式求出C(n , k):
该算法的时间复杂度是多少呢?可以大概的估计下,只填了下三角矩阵,为n*k/2 = n*k,具体的次数为:
按行来填矩阵:算法伪代码:
第1个for是控制行的,要填到第n行。第2个for来控制每行填到哪的,到i和k的较小值。从这2个for也可以看出复杂度是n*k。
实现:
/*计算二项式系数*/
public class Main{
public static void main(String[] args) {
System.out.println("输出8的二项式系数:");
for(int i =0;i <=8;i++)
System.out.println("C"+"("+8+","+ i +")"+" -- "+ Binomial(8,i));
}
public static int Binomial(int n,int k){
int[][] result =newint[n+1][n+1];
for(int i =0;i <= n;i++) //按行来填矩阵
{
for(int j =0;j <= min(i,k);j++)
{
if(j ==0|| j == i)
result[i][j] =1;
else
result[i][j] = result[i -1][j -1] + result[i -1][j];
}
}
return result[n][k];
}
public static int min(int i,int k){
if(i < k)
return i;
return k;
}
}
结果:
动态规划与分治法:
相同点是动态规划和分治法都划分为了较小规模问题的解,不同点是动态规划的较小子问题是交叠的,而且要存储较小子问题的解。
最有子结构和多阶段决策:
最优子结构:有准确的定义,可以参见一些资料,我自己描述下就是:在动态规划求解过程中的,子问题产生的解对于子问题来说也是一个最优解
多阶段决策:一步步的决策,无后效性,决策只依赖于当前状态,不依赖于之前的状态。