动态规划

来源:互联网 发布:ftp传输数据的具体方式 编辑:程序博客网 时间:2024/06/02 05:07

1. 概念

         和分治法(递归)一样,动态规划(dynamicprogramming)是通过组合子问题而解决整个问题的解。

         分治法是将问题划分成一些独立的子问题,递归地求解各子问题,然后合并子问题的解。

         动态规划适用于子问题不是独立的情况,也就是各子问题包含公共的子子问题。

         此时,分治法会做许多不必要的工作,即重复地求解公共的子问题。动态规划算法对每个子问题只求解一次,将其结果保存起来,从而避免每次遇到各个子问题时重新计算答案。

2. 算法设计

两种方法:

         自顶向下(又称记忆化搜索、备忘录):基本上对应着递归函数实现,从大范围开始计算,要注意不断保存中间结果,避免重复计算
         自底向上(递推):从小范围递推计算到大范围
动态规划的重点:
         递归方程+边界条件
3. 爬楼梯问题
         一个人每次只能走一层楼梯或者两层楼梯,问走到第80层楼梯一共有多少种方法。
         设DP[i]为走到第i层一共有多少种方法,那么DP[80]即为所求。很显然DP[1]=1, DP[2]=2(走到第一层只有一种方法:就是走一层楼梯;走到第二层有两种方法:走两次一层楼梯或者走一次两层楼梯)。同理,走到第i层楼梯,可以从i-1层走一层,或者从i-2走两层。很容易得到:
         递推公式:DP[i]=DP[i-1]+DP[i-2]
         边界条件:DP[1]=1   DP[2]=2
         (a)自顶向下的解法:

longlong dp[81] = {0};/*用于保存中间结果

否则会重复计算很多重复的子问题*/

longlong DP(int n)

{

               if(dp[n])

                               return dp[n];

               if(n == 1)

                               return 1;

               if(n == 2)

                               return 2;

               dp[n] = DP(n-1) + DP(n-2);

               return dp[n];  

}

        (b)自底向上的解法:

inti;

longlong dp[81]; /* 注意当n超过75时,结果值将超过int范围 */

dp[1]= 1;

dp[2]= 2;

for(i=3;i <= 80; i++)

               dp[i] = dp[i-1] + dp[i-2];

4. 最长上升子序列
        
对于序列:4 1 2 24,它的最长上升子序列是1 2 4,长度为3
        
对于序列:4 2 4 25 6,它的最长上升子序列是2 4 5 6,长度为4
        
a[i]表示原序列,设DP[i]表示以第i个数结尾的最长上升序列的长度,那么很显然想导出DP[i]的值,需要在DP[k](1<=k<i)中找出满足a[k]<a[i]最大的一项。假设第kk项是我们找到的答案,那么第i个数就可以接在第kk个数之后,成为以第i个数结尾的最长升序列。如果没有找到答案,换言之第i个数比前面的数都要小,那么DP[i]=1,也即生成了从自己开始又以自己结尾的最长升序列。综上,我们很容易得出:
        
递推公式:DP[i]=max(DP[k]+1,DP[i])  1<=k<i
        
边界条件:DP[i]=1                  1<=i<=n
        
算法复杂度为O(n^2)

voidRiseSequence(int Array[], int num)

{

#defineMAX_LENGTH            30

               struct

               {

                               intSequenceValue;  /* max length ending withthis num */

                               intPreviousIndex;    /* record the previousnumber */

               }ArrayInfo[MAX_LENGTH], temp;

               int i;

               for(i = 0; i < num; i++)

               {

                               int j;

                               ArrayInfo[i].SequenceValue= 1;

                               ArrayInfo[i].PreviousIndex= -1;

                               for(j = 0; j <i; j++)

                               {

                                              if(Array[j]< Array[i] && (ArrayInfo[j].SequenceValue + 1 >ArrayInfo[i].SequenceValue))

                                              {

                                                             ArrayInfo[i].SequenceValue= ArrayInfo[j].SequenceValue + 1;

                                                             ArrayInfo[i].PreviousIndex= j;

                                              }

                               }

               }

               temp.SequenceValue =ArrayInfo[0].SequenceValue;

               for(i = 1; i < num; i++)

               {

                               if(temp.SequenceValue< ArrayInfo[i].SequenceValue)

                               {

                                              temp.SequenceValue= ArrayInfo[i].SequenceValue;

                                              temp.PreviousIndex= i;

                               }

               }

               for(i = 0; i <temp.SequenceValue; i++)

               {

                               printf("%d ", Array[temp.PreviousIndex]); /* in reverse order */

                               temp.PreviousIndex= ArrayInfo[temp.PreviousIndex].PreviousIndex;

               }

               printf("\nthe max risingsequence length is %d\n", temp.SequenceValue);

}

0 0