动态规划

137 阅读2分钟

动态规划通用解题步骤

  1. 确定dp数组(dp table)以及下标的含义
  2. 确定递推公式
  3. dp数组如何初始化
  4. 确定遍历顺序
  5. 举例推导dp数组

70. 爬楼梯

核心:第三层只能由第一层迈两步上来,第二层迈一步上来。同时这两种情况还是互斥不重合的。当前情况和之前相关

所有有了状态转移方程dp[i] = dp[i-1] + dp[i-2]

1. 确定dp数组含义

dp[i]表示到达第i层的方式

2. 确定递推公式

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

3. dp数组初始化

dp[1] = 1 dp[2] = 2

4. 确定遍历顺序

自底向上

5. 推导数组看看与实际情况是否相符

dp[1] = 1; [(1)]

dp[2] = 2; [(1,1),(2)]

dp[3] = 3; [(1,2);(1,1,1);(2,1)] ...

6. 代码实现

func climbStairs(n int) int {
    dp := make([]int, n + 1)
    if n < 2 {
        return 1
    }
    dp[1] = 1
    dp[2] = 2
    for i := 3; i < n + 1; i ++ {
        dp[i] = dp[i-1] + dp[i-2]
    } 
    return dp[n]
}

62. 不同路径

1.确定dp的含义

dp[i][j]: 表示从(0,0)出发到达(i,j)的不同路径

2.确定递推公式

只有这两种来源,同时这两种来源是互斥的 dp[i][j] = dp[i-1][j] + dp[i][j-1]

3.dp数组初始化

dp[i][0] = 1 dp[0][j] = 1

4.确定遍历顺序

5.推导dp数组

6.代码实现

func uniquePaths(m int, n int) int {
    // 声明二维数组
    dp := make([][]int, m)
    for i:=0; i < m; i ++ {
        dp[i] = make([]int, n)    
    }
    // 初始化
    for j:=0; j < n; j ++ {
        dp[0][j] = 1
    }
    for i:=0; i < m; i ++ {
        dp[i][0] = 1
    }
    // 根据公式向上算
    for i:=1 ; i < m; i ++ {
        for j := 1; j < n; j++ {
            dp[i][j] = dp[i-1][j] + dp[i][j-1]
        }
    }
    return dp[m-1][n-1]
}

343. 整数拆分

1. 确定dp数组的含义

dp[i]: 对于数字i可以得到的最大乘积dp[i]

2. 确定递推公式

发现dp之间的联系有点难 dp[i]的来源

  1. j * (i-j)---相当于把(i-j)只拆成一个数
  2. j * dp[i-j]--- 相当于把(i-j)拆分成>=2个数
    因此以上为对立事件,涵盖了所有情况,完成了穷举,dp[i]就是记录了最大值,减少了运算次数,减少了穷举数量 dp[i] = max({dp[i], (i - j) * j, dp[i - j] * j})

3. 初始化

dp[2] = 1

4. 确定遍历顺序

5. 手动模拟

输入: n = 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。

输入: n = 3
输出:2
解释:3 = 1 * 2; 3 = 1 * 1 * 1 = 1 * dp[2]

输入: n = 4
输出:2
解释:
4 = 3 * 1; 4 = dp[3] * 1
4 = 2*2; 4 = dp[2] * 2

6. 代码实现

func integerBreak(n int) int {
    dp := make([]int, n + 1)
    dp[1] = 1
    dp[2] = 1
    for i := 3; i < n + 1 ;i ++ {
        for j := 1; j < i   ; j ++ {
            dp[i] = max(dp[i], max(j * (i - j), j * dp[i - j]))
        }
    }   
    return dp[n]
}
func max(num1, num2 int) int {
    if num1 > num2 
        return num1
    }
    return num2
}

96. 不同的二叉搜索树

dp含义

dp[i] 表示i个节点搜索树的种类

递推公式

dp[i]=∑dp[j]∗dp[i−j−1] 其中i中取一个节点为根节点,取j个节点为左子树、剩下的[i-j-1]个节点为右子树

初始化

dp[1]=1

代码

写出来递推公式,代码一般就很简单了..一些四则运算和循环

func numTrees(n int) int {
    dp := make([]int, n + 1)
    dp[1] = 1
    dp [0] = 1
    // 起始值注意一下,代入算一下
    for i := 2; i < n + 1 ; i++{
        for j := 0; j < i; j ++ {
            dp [i] += dp[j] * dp[i-j-1]
        }
    }
    return dp[n]
}