动态规划-算法

记住求过的解来节省时间

记住求解的方式有两种:①自顶向下的备忘录法 ②自底向上。 

只要问题可以划分成规模更小的子问题,并且原问题的最优解中包含了子问题的最优解,则可以考虑用动态规划解决。动态规划的实质是分治思想和解决冗余,因此,动态规划是一种将问题实例分解为更小的、相似的子问题,并存储子问题的解而避免计算重复的子问题,以解决最优化问题的算法策略

 

动态规划和分治法区别:分治法中的各个子问题是独立的(即不包含公共的子子问题),因此一旦递归地求出各子问题的解后,便可自下而上地将子问题的解合并成问题的解。

如果各子问题是不独立的,则分治法要做许多不必要的工作,重复地解公共的子问题。解决上述问题的办法是利用动态规划。

 

例子1:最大子数组和问题

问题

一个有N个整数元素的一维数组(A[0],A[1],…A[N-1]),这个数组有很多子数组,求子数组和的最大值?注意:子数组必须是连续的、不需要返回子数组的具体位置、数组中包含:正、负、零整数、子数组不能空。


穷举法:

  1. int MaxSubStringSum(int *A,int n)  

  2. {  

  3.     int MaxSum = A[0];  

  4.     int sum = 0;  

  5.     for (int i = 0;i < n;i++)  

  6.     {  

  7.         sum = 0;  

  8.         for (int j = i;j < n;j++)  

  9.         {  

  10.             sum += A[j];  

  11.             MaxSum = max(MaxSum,sum);  

  12.   

  13.         }  

  14.     }  

  15.     return MaxSum;  

  16. }  

 

穷取法最为直接,当然耗时也较多,时间复杂度为O(n^2);

 

我们利用穷举法虽然简单易懂,但是其时间复杂度很大,我们试着优化。

 

假设已经知道(A[1],……A[N-1])中和最大的子数组和为MaxSum[1],并且知道,(A[1],……A[N-1])中包含A[1]的和最大的子数组为TempMaxSum[1]。我们就可以把(A[0],……A[N-1])求和最大子数组问题转换为,MaxSum[0] = max{A[0],A[0]+TempMaxSum[1],MaxSum[1]}。

 

  1. int MaxSubStringSum(int *A,int n)  

  2. {  

  3.     int MaxSum = A[0];    

  4.     int TempMaxSum = A[0];  

  5.     for(int i = 1;i < n;i++)  

  6.     {  

  7.         TempMaxSum = max(A[i],TempMaxSum + A[i]);  

  8.         MaxSum = max(MaxSum,TempMaxSum);  

  9.     }  

  10.     return MaxSum;  

  11. }  


 

2.求斐波拉契数列Fibonacci

Fibonacci (n) = 1; n = 0 

Fibonacci (n) = 1; n = 1 

Fibonacci (n) = Fibonacci(n-1) + Fibonacci(n-2)

 

递归算法实现:

public int fib(int n)

{

    if(n<=0)

        return 0;

    if(n==1)

        return 1;

    return fib( n-1)+fib(n-2);

}

//输入6

//输出:8


动态规划-算法_第1张图片

 

 

由于调用每一个函数的时候都要保留上下文,所以空间上开销也不小。这么多的子节点被重复执行,如果在执行的时候把执行过的子节点保存起来,后面要用到的时候直接查表调用的话可以节约大量的时间。下面就看看动态规划的两种方法怎样来解决斐波拉契数列Fibonacci 数列问题。

 

①自顶向下的备忘录法

 

public static int Fibonacci(int n)

{

        if(n<=0)

            return n;

        int []Memo=new int[n+1];        

        for(int i=0;i<=n;i++)

            Memo[i]=-1;

        return fib(n, Memo);

    }

    public static int fib(int n,int []Memo)

    {

 

        if(Memo[n]!=-1)

            return Memo[n];

    //如果已经求出了fib(n)的值直接返回,否则将求出的值保存在Memo备忘录中。               

        if(n<=2)

            Memo[n]=1;

 

        else Memo[n]=fib( n-1,Memo)+fib(n-2,Memo);  

 

        return Memo[n];

    }

 

②自底向上的动态规划

备忘录法还是利用了递归,上面算法不管怎样,计算fib(6)的时候最后还是要计算出fib(1),fib(2),fib(3)……,那么何不先计算出fib(1),fib(2),fib(3)……,呢?这也就是动态规划的核心,先计算子问题,再由子问题计算父问题。

 

更多请参照 算法-动态规划 Dynamic Programming--从菜鸟到老鸟

你可能感兴趣的:(算法,动态规划原理)