动态规划

概念

动态规划:是一种解决问题的思想,大规模问题的结果,是由小规模问题的结果运算得来的。动态规划可用递归来实现(Memorization Search).

算法思想

动态规划其实是运筹学的一种最优化方法,求解动态规划的核心问题是穷举。

暴力的递归解法 -> 带备忘录的递归解法(自顶向下) -> 迭代的动态规划解法(自底向上,遍历枚举最小子问题,一般用数组记录已有解)。

  1. 首先,动态规划的穷举有点特别,因为这类问题存在「重叠子问题」,如果暴力穷举的话效率会极其低下,所以需要「备忘录」或者「DP table」来优化穷举过程,避免不必要的计算。
  2. 而且,动态规划问题一定会具备「最优子结构」,才能通过子问题的最值得到原问题的最值。要符合「最优子结构」,子问题间必须互相独立
  3. 另外,虽然动态规划的核心思想就是穷举求最值,但是问题可以千变万化,穷举所有可行解其实并不是一件容易的事,只有列出正确的「状态转移方程」才能正确地穷举。

动态规划三要素

  1. 重叠子问题
  2. 最优子结构
  3. 状态转移方程

如何列出正确的状态转移方程?

  1. 确定最小极限状态 => 起点
  2. 确定「状态」,也就是原问题和子问题中会变化的变量
  3. 确定「选择」,也就是导致「状态」产生变化的行为
    4、明确 dp 函数/数组的定义。

算法模版

以下以中等-零钱兑换为例:

自顶向下

带备忘录的递归解法, 递归过程中,常用hash表作为记录已有解

/**
 * @param {number[]} coins
 * @param {number} amount
 * @return {number}
 */
var coinChange = function(coins, amount) {
    let len = coins.length;
    // 动态规划自上而下
    let map = new Map();
    function dp (total) {
        if (!total) return 0;
        if (total < 0) return -1;
        if (map.has(total)) return map.get(total);
        let res = Infinity;
        coins.forEach(item => {
            let sub = dp(total - item);
            if (sub === -1) return;
            res = Math.min(res, sub + 1);
            
        });
        res = res === Infinity ? -1 : res;
        map.set(total, res);
        return res;
    }
    return dp(amount);
};

自底向上

迭代的动态规划解法,遍历枚举最小子问题,一般用数组记录已有解,新的解是在已有的解基础上建立

/**
 * @param {number[]} coins
 * @param {number} amount
 * @return {number}
 */
var coinChange = function(coins, amount) {
    // 动态规划自下而上
    let dp = [0];
    for (let i = 1; i <= amount; i ++) { // 遍历枚举,最小子问题, 即为输入的amount
        dp[i] = Infinity;
        coins.forEach(item => {
            if (i - item < 0) return;
            dp[i] = Math.min(dp[i], dp[i - item] + 1);
        });
    }
    return dp[amount] === Infinity ? -1 : dp[amount];
};

相关算法题

简单-斐波那契数
简单-爬楼梯
中等-零钱兑换
中等-三角形最小路径和

矩阵类型

中等-最小路径和
中等-不同路径
中等-不同路径 II

子序列类型

int n = arr.length;
int[][] dp = new dp[n][n];

for (int i = 0; i < n; i++) {
    for (int j = 1; j < n; j++) {
        if (arr[i] == arr[j]) 
            dp[i][j] = dp[i][j] + ...
        else
            dp[i][j] = 最值(...)
    }
}

中等-最长回文子串
中等-最长回文子序列
中等-跳跃游戏

参考

动态规划系列

posted @ 2020-09-12 17:23  fanlinqiang  阅读(240)  评论(0)    收藏  举报