这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战
前两天(其实是七天前),我们讲了初始动态规划,通过爬楼梯认识了动态规划,传送门-># 用爬楼梯来认识动态规划。本章我们来用三道力扣的中等难度提来巩固下我们的学习~话不多说,我们这就开始吧
剑指 Offer II 103. 最少的硬币数目
提示:
1 <= coins.length <= 121 <= coins[i] <= 231 - 10 <= amount <= 104
题解:
确定递归状态,推导递归方程: dp(x) = y。y是所求值,最少硬币个数,个数与金额有关,所以x表示金额。x可以拆解dp(i) + 1,dp(i)表示小于x的某个金额,再加上一张coins中的某个硬币。然后题目要求最少硬币个数,所以dp(i)+1可以转换成min(dp(i1) + coins(j1), dp(i2) + conins(j2),...,dp(in) + coins(jn))。
代码:
/**
* @param {number[]} coins
* @param {number} amount
* @return {number}
*/
var coinChange = function (coins, amount) {
const n = amount + 1;
const dp = new Array(n).fill(-1);
//边界条件
dp[0] = 0;
for (let i = 1; i < n; i++) {
for (let k of coins) {
let j = i - k;
// 金额小于当前硬币金额
if (i < k) continue;
// j金额没有有效性
if (dp[j] === -1) continue;
// 之前值是无效值或者之前值比现在j金额+1数量大,就替换成当前的较小值
if (dp[i] === -1 || dp[i] > dp[j] + 1) dp[i] = dp[j] + 1;
}
}
return dp[amount]
};
152. 乘积最大子数组
题解:
正常来说,dp(x) = y,y表示到x位置的乘积的最大值。dp(x) = min(dp(x-1) * val(x), val(x))。但是这题中有负数,如果负数 * dp(x-1),会变成最小值,而负数 * x-1之前的最小值,会变成x的最大值。所以这题我们需要定义两个数,到i位置的最大值iMax,最小值iMin。然后根据当前所求x位置的值是正是负来判断用最大还是最小值相乘。
代码如下:
/**
* @param {number[]} nums
* @return {number}
*/
var maxProduct = function (nums) {
// 初始化结果为最小值,iMax = iMin = 1,这样dp[0]就等于nums[0]
let ans = -Infinity, iMax = 1, iMin = 1;
for (let i = 0; i < nums.length; i++) {
// 负数就将最大最小值对调
if (nums[i] < 0) {
const temp = iMax;
iMax = iMin;
iMin = temp;
}
// 最大值
iMax = Math.max(nums[i] * iMax, nums[i]);
// 最小值
iMin = Math.min(nums[i] * iMin, nums[i]);
ans = Math.max(iMax, ans);
}
return ans;
};
300. 最长递增子序列
题解:
严格递增子序列表示数组中从小到大的子序列。
我们找递归状态,dp(x) = y,y表示到i位置的最长递增子序列长度,他与最后位置i和之前一个比val(i)小的值的位置的最长递增子序列 + 1得到。
因此我们得到状态转移方程为: dp(i) = dp(j) + 1 && j < i && val(j) < val(i)
代码如下:
/**
* @param {number[]} nums
* @return {number}
*/
var lengthOfLIS = function (nums) {
const n = nums.length;
// 每个i位置的递增子序列最小值肯定为1,初始化后dp[0] = 1
const dp = new Array(n).fill(1);
let ans = 1;
for (let i = 1; i < n; i++) {
// 遍历i位置之前的元素,找到合法的递增子序列
for (let j = 0; j < i; j++) {
// 必须要j位置的值小于i位置的值才是递增
if (nums[j] < nums[i]) {
// dp[i]等于所有j位置的递增子序列长度+1中的最大值。
dp[i] = Math.max(dp[j] + 1, dp[i])
}
}
// ans等于所有以i为结尾的递增子序列长度中的最大值
ans = Math.max(dp[i], ans)
}
return ans;
};
这道题还有个进阶,因为上面的方法的时间复杂度是O(n2),进阶要我们降低到O(nlogn),大家有什么想法欢迎留言讨论~下一篇递归文章我们来给出答案。
觉得对您有帮助麻烦点个赞再走~
本文的代码已收录Github,仓库中包括之前的文章链接和代码收录,后续代码也会陆续更新,欢迎大家不吝赐教。