经典的动态规划问题(斐波那契数列)
使用动态规划算法实现斐波那契数列(时间复杂度O(n)),使用穷举法实现斐波那契数列(时间复杂度O(2^n))
如何使用动态规划实现斐波那契数列呢?
算法思想:
我们求解斐波那契数列,最先想到的当然是使用递归方法啦!但是我们来思考一个问题,如果我们要求解Fb(6)
我们可以看出,对于右边的数我们在左边已经求得,在使用递归算法的时候我们右边的数又重新计算了一次复杂度就乘以了2,所以计算的时间复杂度为O(2^n)。试想一下如果我们把之前算的数用数组存放起来是不是时间复杂度就提高了呢?答案是显然的。这就引出动态规划的一个重要概念:最优子结构。我们把问题的求解划分为子问题的最优解,然后通过子问题的最优解来求解。但是怎么前面的解对求整体最优解有效呢?我们又引出了动态规划的第二个重要特性:子问题的重叠。子问题的重叠是能不能使用动态规划的一个重要条件,因为动态规划实质上是一种以时间换取空间的算法,我们需要在内存上开辟一个空间存放它子问题的解,以便以后出现重叠时使用。那么如何保证我们记录的解是有效的呢?这就引出了动态规划的第三个概念:无后效性。我们必须保证每个状态(解)是对过去的一个完整的总结,这样以后在使用这个状态时才不会对后续计算产生影响。
Talk is cheap,let’s show code
//蛮力算法(递归)
public static int Fb(int n){
if(n==1||n==2){
return 1;
}else{
return Fb(n-1)+Fb(n-2);
}
}
//动态规划的算法(以空间换取时间)
public static int[] dpFb(int n){
int a[]=new int[n];
a[0]=1;
a[1]=1;
for(int i=2;i<n;i++){
a[i]=a[i-1]+a[i-2];
}
return a;
}
下面我们来解决动态规划的另外一个问题:最长子序列的问题
我们首先了解一下什么是子序列
有字符串X=<x1,x2,x3……xm>
对于字符串Z=<z1,z2,z3……zk>
若存在X的元素产生的一个严格递增的序列(必须严格递增,选可以间隔的选)
Zj=xi i=1,2….k
那么我们就称Z为X的一个子序列。
对于两个字符串序列:
X=<x1,x2,x3……xm> 第一个字符串
Y=<y1,y2,y3……yn> 第二个字符串
若一个序列既是X的子序列又是Y的子序列则称为他们的公共子序列
算法思想:
我们要找出最大的子序列存在两种算法(第一种:蛮力算法;第二种:动态规划算法),下面我们来一一讨论:
蛮力算法:
不妨设|m|<|n|,那么我们就从X的集合中开始抽取元素生成子序列,总共有2^m个子序列,然后再在Y的集合中进行扫描,一次扫描n个元素,总共要扫描2^m次,时间复杂度为O(n*2^m)
动态规划算法
图中黄色边界表示我们需要求解的最后问题的解,图中红色边框就是我们的最优子结构
下面我们来讨论不同的子问题(这里我们设计到一个选和不选的问题,这个思想在动态规划思想里面很重要,类似于数学中的分段函数)
1、若xm=yn,
Z(k-1)是X(m-1)和Y(n-1)的LCS
2、若xmyn,不考虑xm进入公共子序列,即xmzk;
Z是X(m-1)和Y(n)的LCS
3、若xmyn,考虑xm进入公共子序列,即yn=zk;
Z是X(m)和Y(n)的LCS
优化函数的递推方程:
Talk is cheap,let’s show code
动态规划算法::
public class dp {
public static int f(String str1,String str2){
char a[]=str1.toCharArray();
char b[]=str2.toCharArray();
int c[][]=newint [a.length][b.length];
int max=0;
for(int i=1;i<c.length;i++){
for(int j=1;j<c[i].length;j++){
if(a[i-1]==b[j-1]){
c[i][j]=c[i-1][j-1]+1;
if(c[i][j]>max){
max=c[i][j];
}
}
}
}
return max;
}
public static void main(String[] args){
int n = f("abcdkkk", "baabcdadabc");
System.out.println(n);
}
}