定义
动态规划,简称dp,即将问题分解为互相重叠的字问题,通过反复求解子问题来解决原问题。
求解动态规划的核心问题是穷举,这类问题因为存在重叠子问题,穷举效率会极其低下。
动态规划问题会存在最优子问题,可以通过子问题的最值来得到原问题的最值,然后推导出状态转移方程(状态转移方程,是动态规划中本阶段的状态。往往是上一阶段状态和上一阶段决策的结果。如果给定了第K阶段的状态Sk以及决策uk(Sk),则第K+1阶段的状态Sk+1也就完全确定。)
动态规划的解题方法
- 递归 + 记忆化(自顶向下)
- 动态规划(自底向上)
解题流程
- 根据重叠子问题定义状态
- 寻找最优子结构推导状态转移方程
- 确定dp初始状态
- 确定输出值
leetcode
解法:递归 + 记忆化
- Leetcode 70 爬楼梯
解题思路:
爬上 第 n 阶楼梯的方法数量 = 爬上 n-1 阶 楼梯的数量 + 爬上 n-2 阶楼梯的数量。
因为每次最多爬 1 阶 或 2阶。
倒推过去,爬上 n-1 阶 楼梯再爬一阶可以到第 n 阶楼梯,爬上 n-2 阶 楼梯再爬 2阶可以到 第 n 阶楼梯。两者相加正好是 爬到第 n 阶 楼梯的方法数量。这两部分再继续倒推就可以得到自己的数量。
这样就得到了 状态转移方程 dp[n] = dp[n-1] + dp[n-2];
var climbStairs = function (n) {
const memo = {} ; // 缓存结果
const helper = (x) => {
if (memo[x]) return memo[x];
if (x === 1) return 1;
if (x === 2) return 2;
memo[x] = helper(x-1) + helper(x-2);
return memo[x];
}
return helper(n);
}
解法:动态规划
-
Leetcode 121 买股票的最佳时机
解题思路
每天的状态是前一台的状态 + 今天的状态结合而成的,由于每天有两种状态,用一个矩阵来表示。
第i-1天不持有然后不操作 和 第i-1天持有然后卖出
dp[i][0] 今天不持有 = Math.max( dp[i-1][0] 昨日持有今日不操作, dp[i - 1][0] + prices[i] 昨日不持有今日买入)
第i-1天持有然后不操作 和 第i-1天不持有然后买入
dp[i][1] 今日持有 = Math.max(dp[i-1][1] 昨日持有今日不操作, -prices[i] 昨日持有今日卖出)
const maxProfit = function (prices) {
let n = prices.length;
let dp = Array.from(new Array(n), () => new Array(2));
dp[0][0] = 0; //第0天不持有
dp[0][1] = -prices[0]; //第0天持有
for (let i = 1; i < n; i++) {
// 第i-1天不持有然后不操作 和 第i-1天持有然后卖出 两种情况的最大值转移过来
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
// 第i-1天持有然后不操作 和 第i-1天不持有然后买入 两种情况的最大值转移过来
dp[i][1] = Math.max(dp[i - 1][1], -prices[i]);
}
return dp[n - 1][0];
};
-
leetcode 53 最大子数组和
解题思路
如果以第i-1个数结尾的子数组最大和小于0,就重新开始计算新的子数组。
得到状态转移公式:
当前最大子数组之和 dp[i] = Math.max( 前一项子数组之和 dp[i-1] + 当前项 nums[i] ,当前项nums[i] )
var maxSubArray = function(nums) {
let pre = 0, maxAns = nums[0]; // 以前一项结尾的数组的最大子数组之和, 最大值
nums.forEach((x) => {
pre = Math.max(pre + x, x); // 当前项是否作为新的子数组的第一个元素。
maxAns = Math.max(maxAns, pre); // 取最大值
});
return maxAns;
};
-
198 打家劫舍
解题思路
因为不能去两家相邻的房屋,所以。
dp[0] = nums [0];
dp[1] = Max.max(nums[0], nums[1]);
dp[2] = Math.max(dp[1], dp[0] + dp[2]);
得到状态转移公式
dp[n] = Math.max(dp[n-1], dp[n-2]+nums[n]);
var rob = function(nums) {
const len = nums.length;
const dp = [nums[0], Math.max(nums[0], nums[1])];
for (let i=2; i< len; i++) {
dp[i] = Math.max(dp[i-1], dp[i-2]+nums[i]);
}
return dp[len-1]
};