五大常用算法(二) - 动态规划算法

动态规划算法

基本思想

动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。

动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。
如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。具体的动态规划算法多种多样,但它们具有相同的填表格式


动态规划算法与分治法最大的差别是:适合于用动态规划法求解的问题,经分解后得到的子问题往往不是互相独立的(即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解)


应用场景:

适用动态规划的问题必须满足最优化原理、无后效性和重叠性。
1. 最优化原理(最优子结构性质) 最优化原理可这样阐述:一个最优化策略具有这样的性质,不论过去状态和决策如何,对前面的决策所形成的状态而言,余下的诸决策必须构成最优策略。简而言之,一个最优化策略的子策略总是最优的。一个问题满足最优化原理又称其具有最优子结构性质。

2. 无后效性 将各阶段按照一定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策,而只能通过当前的这个状态。换句话说,每个状态都是过去历史的一个完整总结。这就是无后向性,又称为无后效性。

3. 子问题的重叠性 动态规划将原来具有指数级时间复杂度的搜索算法改进成了具有多项式时间复杂度的算法。其中的关键在于解决冗余,这是动态规划算法的根本目的。动态规划实质上是一种以空间换时间的技术,它在实现的过程中,不得不存储产生过程中的各种状态,所以它的空间复杂度要大于其它的算法。


实例:

走台阶

有n级台阶,一个人每次上一级或者两级,问有多少种走完n级台阶的方法。

分析:动态规划的实现的关键在于能不能准确合理的用动态规划表来抽象出实际问题
我们用 dp[n] 来表示动态规划表,dp[i] 表示到达 i 级台阶的方法数(0<i<=n)。

n为1时,dp[n] = 1;
n为2时,dp[n] = 2;
那么当我们要走上n级台阶,必然是从n-1级台阶迈一步,或者是从n-2级台阶迈两步。所以到达n级台阶的方法数必然是到达n-1级台阶的方法数加上到达n-2级台阶的方法数之和。即 dp[n] = dp[n-1] + dp[n-2]

public class CalculationSteps {
   
    //动态规划表step[n],用来记录到达i级台阶的方法数
    public static int[] steps = new int[11];

    //计算到达i级台阶的方法数
    public static int calStep(int n){
   
        if(n==1 || n==2)     return n;
        if(steps[n-1]==0)    steps[n-1] = calStep(n-1);   //计算到达n-1级台阶的方法数
        if(steps[n-2]==0)    steps[n-2] = calStep(n-2);   //计算到达n-2级台阶的方法数
        return steps[n-1] + steps[n-2];
    }
    
    public static void main(String[] args) {
   
        steps[10] = calStep(10);
        for (int i = 0; i < steps.length; i++) {
   
            System.out.print(steps[i]+" ");
        }
    }
}

运行结果:
0 1 2 3 5 8 13 21 34 55 89


矩阵最小路径和

给定一个矩阵M,从左上角开始每次只能向右走或者向下走,最后达到右下角的位置。路径中所有数字累加起来就是路径和,返回所有路径的最小路径和。

分析:假设M是m行n列的矩阵,那么我们用 dp[m][n] 来抽象这个问题,dp[i][j]表示的是从原点到 (i,j) 位置的最短路径和。

首先计算原点,直接返回;
第一行和第一列,直接累加即可;
对于其他位置,要么是从它左边的位置达到,要么是从上边的位置达到,我们取左边和上边的较小值。然后加上当前的路径值,就是达到当前点的最短路径。

public class MinSteps {
   
    //动态规划表step[i][j],用来记录从原点到 (i,j) 位置的最短路径和。
    public static int[][] steps = new int[4][4];

    public static int minSteps(int[][] arr,int row,int col){
   
        //如果为原点,则直接返回
        if(row==0 && col==0){
   
            steps[row][col] = arr[row][col];
            return steps[row][col];
        }
        
        //计算到arr[row][col]的左面位置的值、上面位置的值
        if(col>=1 && steps[row][col-1]==0)   steps[row][col-1] 
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值