动态规划

动态规划–解题技巧

一、适用条件

具有重叠子问题

子问题是不独立的,并且在后续的计算中也可能会多次用到,子问题也能按照相同的方法分割为更小的子问题。

满足最优子结构

某阶段状态一旦确定,就不受这个状态以后决策的影响,即某状态以后的国产不会影响曾经的状态,仅仅于当前状态有关。

无后效性

每个子问题的决策不能对后面未解决的问题产生影响。

二、动态规划的三大步骤

第一步:定义数组元素的含义

一般情况下使用一维数组或者二维数组保存数据,需要规定该数组代表的含义,比如dp[ i ]代表什么意思。

第二步:找出数组元素之间的关系式(状态转移方程)

当我们要计算dp[ n ]时,可以利用dp[ n-1 ]、dp[ n-2 ]、… dp[ 1 ]来推出,例如dp[ n ] = dp[ n-1 ] + dp[ n-2 ],这是最难的一步也是最关键的一步。

第三步:寻找初始值

根据限制条件推导出初始条件。

三、常见的动态规划题

案例1:简单的一维DP

青蛙跳台阶:

  1. 定义数组元素dp[ i ]的含义:表示跳到i级台阶有dp[ i ]种跳法。
  2. 状态转移方程:dp[ n ] = dp[ n-1 ] + dp[ n-2 ]
  3. 初始条件:dp[0] = 0,dp[1] = 1,dp[2] = 2
int f(int n) {
	if(n <= 2)
        return n;
    int[] dp = new int[n+1];
    int f1 = 1, f2 = 2;
    int i = 3;
    while(i <= n) {
        dp[i] = dp[i-1] + dp[i-2];
        i++;
    }
    return dp[n];
}

案例2:二维数组的DP

问题描述:一个机器器人位于一个 m x n ⽹网格的左上⻆角)。机器器⼈每次只能向下或者向右移动一步。机器⼈人试图达到⽹网格的右下⻆角。 问总共有多少条不同的路径?

  1. 定义数组元素dp[ i ] [ j ]的含义:当机器人从左上角走到(i,j)时,一共有dp[ i ] [ j ]种路劲。
  2. 状态转移方程:dp[ i ] [ j ] = dp[ i-1 ] [ j ] + dp[ i ] [ j-1 ]
  3. 定义初始值:dp[ 0 ] [ 0,,j,,n-1 ] = 1,dp[ 0,,j,,m-1 ] [ 0 ] = 1
uniquePaths(int m, int n) {
    if(m <= 0 || n <= 0)
        return 0;
    int[][] dp = new int[m][n];
    
    for(int i = 0;i < m;i++) 
        dp[i][0] = 1;
    for(int j = 0;j < n;j++)
        dp[0][j] = 1;
    
    for(int i = 1;i < m;i++) {
        for(int j = 1;j < n;j++) {
            dp[i][j] = dp[i-1][j] + dp[i][j-1];
        }
    }
    return dp[m-1][n-1];
}

四、动态规划如何优化

在使用数组时,对于不会在用到的值就不需要保存,可以减少空间复杂度。比如案例2中当我们计算第i行的值时只会用到第i-1行的值,对于第1行至i-2行的值没有用到,所以可以只使用一个一维数组来保存上一行的值,在计算的过程中,需要不断更新dp[]的值。

全部评论

相关推荐

安静的垂耳兔在泡澡:ks已经第八次投递了,它起码挂了还让你再投,不错了
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务