动态规划小结

Dynamical Programming(动态规划)

这是一种对递归方法的优化,应用地最多的场合是组合优化问题(Combinatorial Optimization)。

在用一般递归方法解决某些问题的时候,会出现对同一个子问题的重复计算,例如:

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

f(10) = f(9) + f(8) =  (f(8) + f(7)) + f(8) = ...

例如上面的f(8)就被计算了很多次。 所以,动态规划实现了,在解决子问题的过程中,保存这些子问题的答案,供后面直接使用,避免重复计算。

 

动态规划的例子包括:

1、最长公共子序列问题(L[i,j])

2、矩阵链相乘的最小代价问题(C[i, j])

3、图的最短路径问题(Floyd算法)

4、背包问题 (V[i, j])

 

以上例子基本都是通过二维矩阵来保存每次递归计算的中间结果的。

而且这个中间结果也是子问题的最优解。

----

动态规划要点

1. 问题怎么表示:整理入参,形成一个向量。例如,只有一个入参,那么问题可以表示为 answer[k] (为什么是数组,因为把子问题也表示了)。
   有的人也把子问题叫做“状态”。
2. 结果怎么表示:例如,表示成一个向量。问题的有些部分看似是变量,其实每次运行的时候可以视为固定的、是常量。
3. 问题怎么分解:针对入参进行分解,让入参变小。。分解之后,往往可以写出一个递推公式(状态转换)。
   先弄清楚问题是几维的,一维f(n), 二维f(i, j)
   不同问题之间的区别主要体现在这里,即如何分解。
   分解的时候可以逆向思考,即假如已经得到最终结果n,他跟上一步结果n-1之间是什么关系。
   进行问题分解的时候,往往可以在一个问题的末尾(例如某个序列的最后一个元素的地方)执行分类讨论。
   分解问题的时候可能产生一个新问题,不过这个新问题的限制条件更多,更容易解决。
4. 根据这个递推公式,采用自底向上的方法,写代码进行实现。一般是一个循环,从小到大计算出最终的问题。


最长递增子序列问题的解
L(i) = max(L(i-1), S(i));
S(i) = for k=0 to i-1: (a[k] < a[i] ? S(i - k) + 1 : 1);

说明:L(i)是最长递增子序列长度, S(i)是必须包含元素i的,a[1..i]范围内的最长递增子序列(比L(i)多一个条件,必须包含a[i]这个数组元素)

int lis(int[] nums) {
    int n = nums.length;
    int[] L = new int[n]; 
    int[] S = new int[n];

    L[0] = S[0] = 1;

    for ( int i = 1; i < n; ++i ) {
       S[i] = 1;
       for ( int j = 0; j < i; ++j ) {
           if ( nums[j] < nums[i] ) {
               S[i] = max(S[j] + 1, S[i]);
            }
        }

        L[i] = max(L[i - 1], S[i]);
    }

   return L[i];
}

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值