动态规划

动态规划–解题技巧

一、适用条件

具有重叠子问题

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

满足最优子结构

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

无后效性

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

二、动态规划的三大步骤

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

一般情况下使用一维数组或者二维数组保存数据,需要规定该数组代表的含义,比如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[]的值。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值