509. 斐波那契数
斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给定 n ,请计算 F(n) 。
不要用递归
//暴力递归复杂度O(2^n)
var fib = function (N) {
if (N == 0) return 0;
if (N == 1) return 1;
return fib(N - 1) + fib(N - 2);
};
//时间复杂度O(n)
var fib = function(n) {
if (n <= 1) return n;
const dp = [0, 1]
for(let i = 2;i <= n;i++){
dp[i] = dp[i-1] + dp[i-2]
}
return dp[n]
};
62. 不同路径 (medium)
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
输入: m = 3, n = 7
输出: 28
公式:dp[i][j] = dp[i-1][j] + dp[i][j-1]
var uniquePaths = function(m, n) {
let dp = new Array(m).fill(0).map(() => new Array(n).fill(0))
for (let i = 0; i < m; i++){
dp[i][0] = 1;
}
for (let j = 0; j < n; j++){
dp[0][j] = 1;
}
for (let i = 1;i < m; i++){
for (let j = 1; j < n; j++){
dp[i][j] = dp[i-1][j] + dp[i][j-1]
}
}
return dp[m-1][n-1]
};
压缩空间:dp[j] = dp[j-1] + dp[j]
var uniquePaths = function(m, n) {
let dp = new Array(n).fill(1)
for (let i = 1; i<m; i++){
for(let j = 1; j < n ;j++){
dp[j] = dp[j-1] + dp[j]
}
}
return dp[n-1]
};
63. 不同路径 II
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。
输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右
dp[i][j] = obstacleGrid[i][j] === 1 ? 0 : dp[i-1][j] + dp[i][j-1]
var uniquePathsWithObstacles = function(obstacleGrid) {
const height = obstacleGrid.length;
const weight = obstacleGrid[0].length;
let dp = new Array(height).fill(0).map(() => new Array(weight).fill(0))
for (let i = 0; i < height && obstacleGrid[i][0] === 0;i++){
dp[i][0] = 1;
}
for (let j = 0; j < weight && obstacleGrid[0][j] === 0;j++){
dp[0][j] = 1;
}
for(let i = 1; i < height; i++){
for (let j = 1; j < weight; j++){
dp[i][j] = obstacleGrid[i][j] === 1 ? 0 : dp[i-1][j] + dp[i][j-1]
}
}
return dp[height-1][weight-1]
};
70. 爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
输入: n = 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
var climbStairs = function(n) {
let dp = new Array(n).fill(0);
dp[0] = 1;
dp[1] = 2;
for (let i = 2; i < n; i++){
dp[i] = dp[i-1]+dp[i-2];
}
return dp[n-1]
};
279. 完全平方数
给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。完全平方数 是一个整数,其值等于另一个整数的平方;
换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
输入: n = 12
输出: 3
解释: 12 = 4 + 4 + 4
dp[i] = min(dp[i],dp[i-j*j]+1)
这题注意的是:i-j*j>=0
var numSquares = function(n) {
let dp = new Array(n+1).fill(0);
for(let i = 1 ;i <n+1; i++){
dp[i] = i;
for(let j = 1; i-j*j >= 0;j++){
dp[i] = Math.min(dp[i],dp[i-j*j]+1)
}
}
return dp[n]
};
120. 三角形最小路径和
给定一个三角形 triangle ,找出自顶向下的最小路径和。
每一步只能移动到下一行中相邻的结点上。
相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。
也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1 。
输入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]]
输出:11
解释:如下面简图所示:
2
3 4
6 5 7
4 1 8 3
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
这道题目必须要自下而上来求:dp[i][j] = Math.min(dp[i+1][j],dp[i+1][j+1])+triangle[i][j]
var minimumTotal = function(triangle) {
const h = triangle.length;
let dp = new Array(h);
for (let i = 0; i < h; i++){
dp[i] = new Array(triangle[i].length)
}
for(let i = h - 1; i >= 0; i--){
for (let j = 0; j < triangle[i].length; j++) {
if(i === h - 1){
dp[i][j] = triangle[i][j]
}else{
dp[i][j] = Math.min(dp[i+1][j],dp[i+1][j+1])+triangle[i][j]
}
}
}
return dp[0][0]
};
152. 乘积最大子数组 (medium)
给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
测试用例的答案是一个 32-位 整数。
子数组 是数组的连续子序列。
输入: nums = [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
这道题最要先考虑的是数字有正负号。
我们使用dp[i-1][0]表示数组前i-1个里面最小的乘积,dp[i-1][1]表示数组浅i-1个里面最大的乘机。
dp[i][0]=min(dp[i-1][0]*nums[i],dp[i-1][1]*nums[i],nums[i])
dp[i][1]=max(dp[i-1][0]*nums[i],dp[i-1][1]*nums[i],nums[i])
- prevMin = Math.min(prevMin * num[i], prevMax * num[i], nums[i])
- prevMax = Math.max(prevMin * num[i], prevMax * num[i], nums[i])
var maxProduct = function(nums) {
const len = nums.length;
let res = nums[0];
let dp = new Array(len).fill(0).map( ()=> new Array(2).fill(0));
dp[0][0] = dp[0][1] = nums[0];
for(let i = 1; i < len; i++){
dp[i][0] = Math.min(dp[i-1][0]*nums[i],dp[i-1][1]*nums[i],nums[i])
dp[i][1] = Math.max(dp[i-1][0]*nums[i],dp[i-1][1]*nums[i],nums[i])
res = Math.max(dp[i][1], res)
}
return res
};
121. 买卖股票的最佳时机 限定交易次数 k=1
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
分析:
首先我们要去定只有买入的时候k会改变
dp[i][k][0] 第i天已经操作了k次手上没有股票
dp[i][k][1] 第i天已经操作了k次手上有股票
dp[i][k][0] = max(dp[i-1][k][0],dp[i-1][k][1]+prices)
- 两种情况:
-
前一天也没有股票。 -
前一天有股票今天卖掉了(说明前一天有股票已经是操作的第二次了)
dp[i][k][1] = max(dp[i-1][k][1],dp[i-1][k-1][0]-prices[i])
- 两种情况:
-
前一天有股票。 -
前一天没有股票今天买入(说明今天是操作的第二次今天k变成2,昨天还是操作的第一次所以是k-1)
这题与k无关 dp[i][0] = max(dp[i-1][0],dp[i-1][1]+prcies[i]) dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices) = max(dp[i-1][1], -prices[i])
var maxProfit = function(prices) {
const n = prices.length;
let dp = new Array(n).fill(0).map(()=>new Array(2).fill(0));
dp[0][0] = 0;
dp[0][1] = -prices[0];
for (let i = 1; i < n; i++){
dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]+prices[i])
dp[i][1] = Math.max(dp[i-1][1], -prices[i])
}
return dp[n-1][0]
};
122. 买卖股票的最佳时机 II 交易次数无限制 k = +infinity
给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润
输入:prices = [7,1,5,3,6,4]
输出:7
解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3 。
总利润为 4 + 3 = 7 。
k是无限次,即与k无关
dp[i][0] = max(dp[i-1][0],dp[i-1][1]+prices)
dp[i][1] = max(dp[i-1][1],dp[i-1][0]-prices[i])
d[0][0]=0,d[0][1]=-prices[0]
var maxProfit = function(prices) {
const len = prices.length;
let dp = new Array(len).fill(0).map(()=>new Array(2).fill(0))
dp[0][0] = 0;
dp[0][1] = -prices[0];
let res = 0
for(let i = 1;i < len;i++){
dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]+prices[i]);
dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0]-prices[i]);
res = Math.max(res,dp[i][0])
}
return res
};
123. 买卖股票的最佳时机 III 限定交易次数 k=2
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
输入:prices = [3,3,5,0,0,3,1,4]
输出:6
解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。
随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。
dp[i][2][0] = Math.max(dp[i-1][2][0],dp[i-1][2][1]+prices[i])
dp[i][2][1] = Matn.max(dp[i-1][2][1],dp[i-1][1][0]-prices[i])
dp[i][1][0] = Math.max(dp[i-1][1][0],dp[i-1][1][1]+prices[i])
dp[i][1][1] = Math.max(dp[i-1][1][1],-prices[i])
-->i只与i-1有关
dp[2][0] = Math.max(dp[2][0],dp[2][1]+prices[i])
dp[2][1] = Matn.max(dp[2][1],dp[1][0]-prices[i])
dp[1][0] = Math.max(dp[1][0],dp[1][1]+prices[i])
dp[1][1] = Math.max(dp[1][1],-prices[i])
-->简化
sell_2 = Math.max(sell_2,buy_2+prices[i])
buy_2 = Math.max(buy_2,sell_1-prices[i])
sell_1 = Math.max(sell_1,buy_1+prices[i])
buy_1 = Math.max(buy_1,-prices[i])
let buy_1 = -prices[0], sell_1 = 0
let buy_2 = -prices[0], sell_2 = 0
var maxProfit = function(prices) {
let buy_1 = -prices[0], sell_1 = 0
let buy_2 = -prices[0], sell_2 = 0
const n = prices.length
for(let i = 1;i<n;i++) {
sell_2 = Math.max(sell_2,buy_2+prices[i])
buy_2 = Math.max(buy_2,sell_1-prices[i])
sell_1 = Math.max(sell_1,buy_1+prices[i])
buy_1 = Math.max(buy_1,-prices[i])
}
return sell_2
};
188. 买卖股票的最佳时机 IV 限定交易次数 最多次数为 k
给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
注意:数组大小是K+1
var maxProfit = function(k, prices) {
const n = prices.length
let dp = new Array(k+1)
for(let i = 0; i <= k; i++){
dp[i] = {
sell : 0,
buy : -prices[0]
}
}
for(let i = 1; i < n; i++){
for(let j = 1; j <= k; j++){
dp[j] = {
sell:Math.max(dp[j].sell, dp[j].buy+prices[i]),
buy:Math.max(dp[j].buy, dp[j - 1].sell-prices[i])
}
}
}
return dp[k].sell
};
714. 买卖股票的最佳时机含手续费 每次交易含手续费+无限次
给定一个整数数组 prices,其中 prices[i]表示第 i 天的股票价格 ;整数 fee 代表了交易股票的手续费用。
你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。
返回获得利润的最大值。
注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。
输入:prices = [1, 3, 2, 8, 4, 9], fee = 2
输出:8
解释:能够达到的最大利润:
在此处买入 prices[0] = 1
在此处卖出 prices[3] = 8
在此处买入 prices[4] = 4
在此处卖出 prices[5] = 9
总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8
其实就是在买入的时候要减去交易费
卖出的时候扣手续费
k是无限次,即与k无关
dp[i][0] = max(dp[i-1][0],dp[i-1][1]+prices)
dp[i][1] = max(dp[i-1][1],dp[i-1][0]-prices[i])
简化
sell = Math.max(sell,buy+prices-fee)
buy = Math.max(buy, sell-prices)
sell = 0
buy = -prices[0]
var maxProfit = function(prices, fee) {
const n = prices.length;
let buy = -prices[0];
let sell = 0;
for(let i = 0;i<n;i++){
sell = Math.max(sell,buy+prices[i]-fee)
buy = Math.max(buy, sell-prices[i])
}
return sell
};
309. 最佳买卖股票时机含冷冻期
给定一个整数数组prices,其中第 prices[i] 表示第 i 天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
输入: prices = [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
注意是:i-2
dp[i][k][0] = Math.max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i])
dp[i][k][1] = Math.max(dp[i - 1][k][1], dp[i - 2][k - 1][0] - prices[i])\
//降维i
dp[0] = Math.max(dp[0], dp[1] + prices[i])
dp[1] = Math.max(dp[1], profit_freeze - prices[i])
const maxProfit = function (prices) {
let n = prices.length;
let buy = -prices[0];//手中有股票
let sell = 0;//没有股票
let profit_freeze = 0;
for (let i = 1; i < n; i++) {
let temp = sell;
sell = Math.max(sell, buy + prices[i]);
buy = Math.max(buy, profit_freeze - prices[i]);
profit_freeze = temp;
}
return sell;
};
322. 零钱兑换
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
你可以认为每种硬币的数量是无限的。
输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1
定义dp[i]表示凑成金额i需要最少多少枚硬币
dp[i] = Matn.min(dp[i],dp[i-coin]+1)
var coinChange = function(coins, amount) {
let dp = new Array(amount+1).fill(Infinity);
dp[0] = 0
for(let i = 1; i <= amount;i++){
for(let x of coins){
if(i - x>= 0){
dp[i] = Math.min(dp[i], dp[i-x]+1)
}
}
}
return dp[amount] === Infinity ? -1 : dp[amount];//如果dp[amount] === Infinity,则无法兑换
};
72. 编辑距离
给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
插入一个字符
删除一个字符
替换一个字符
输入:word1 = "horse", word2 = "ros"
输出:3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')
因为要求最短距离,所以我们定义dp[i][j]表示word1前i个字母和word2前j个字母需要的最短的距离
dp[i][j] = Math.min(dp[i-1][j-1]+1,dp[i-1][j]+1,dp[i-1][j-1]+1,dp[i][j-1]+1)
var minDistance = function(word1, word2) {
const len1 = word1.length;
const len2 = word2.length;
let dp = new Array(len1+1).fill(0).map(()=>new Array(len2+1).fill(0));
//初始化数组,word1前i个字符最少需要i次操作,比如i次删除变成word2
for (let i = 1; i <= word1.length; i++) {
dp[i][0] = i;
}
//初始化数组,word2前i个字符最少需要i次操作,比如j次插入变成word1
for (let j = 1; j <= word2.length; j++) {
dp[0][j] = j;
}
for(let i = 1;i<=len1;i++){
for(let j=1;j<=len2;j++){
if (word1[i - 1] === word2[j - 1]) {
//如果word1[i-1] === word2[j-1],说明最后一个字符不用操作。
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + 1);
}
}
}
return dp[len1][len2]
};
10. 正则表达式匹配
给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。
'.' 匹配任意单个字符
'*' 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
输入: s = "aa", p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。
不会
312. 戳气球
有 n 个气球,编号为0 到 n - 1,每个气球上都标有一个数字,这些数字存在数组 nums 中。
现在要求你戳破所有的气球。戳破第 i 个气球,你可以获得 nums[i - 1] * nums[i] * nums[i + 1] 枚硬币。 这里的 i - 1 和 i + 1 代表和 i 相邻的两个气球的序号。如果 i - 1或 i + 1 超出了数组的边界,那么就当它是一个数字为 1 的气球。
求所能获得硬币的最大数量。
输入:nums = [3,1,5,8]
输出:167
解释:
nums = [3,1,5,8] --> [3,5,8] --> [3,8] --> [8] --> []
coins = 3*1*5 + 3*5*8 + 1*3*8 + 1*8*1 = 167
dp[i][j]表示区间能拿到的最多钱数,都是从中间k位置戳气球
按照意要有虚拟气球:let points = [1, ...nums, 1]; //两边添加虚拟气球
1=<k<=n,k表示能戳的位置(已经有虚拟气球了)
var maxCoins = function(nums) {
const newNums = [1,...nums,1];
const n = nums.length;
const dp = Array.from(Array(n + 2), () => Array(n + 2).fill(0));
for(let i = n;i >= 0; i--){
for(let j = i + 1;j < n + 2;j++){
for(let k = i + 1; k < j; k++){
dp[i][j] = Math.max(dp[i][j], dp[i][k]+dp[k][j]+newNums[i]*newNums[k]*newNums[j])
}
}
}
return dp[0][n+1]
};
343. 整数拆分
给定一个正整数 `n` ,将其拆分为 `k` 个 **正整数** 的和( `k >= 2` ),并使这些整数的乘积最大化。
返回你可以获得的最大乘积。
输入: n = 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。
dp[i]表示整数i拆分的最大乘积
var integerBreak = function(n) {
let dp = new Array(n+1).fill(0);
dp[2] = 1;
for(let i = 3;i < n+1;i++){
for(let j = 1;j < i; j++){
dp[i] = Math.max(dp[i],dp[i-j]*j,(i-j)*j)
}
}
return dp[n]
};
0-1背包问题
0-1背包问题指的是有`n`个物品和容量为`j`的背包,`weight`数组中记录了`n`个物品的重量,位置`i`的物品重量是weight[i],`value`数组中记录了`n`个物品的价值,位置i的物品价值是`vales[i]`,每个物品只能放一次到背包中,问将那些物品装入背包,使背包的价值最大。
dp[i][j]表示前i个物品装满j的背包的最大价值。dp[i][j]取决于前
i-1中的物品装入容量为j的背包中的最大价值
两种情况:
1、i号物品放不进去背包了:dp[i][j] = dp[i-1][j]
2、i号物品能放得进去背包:dp[i][j] = max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i])
function testWeightBagProblem(wight, value, size) {
const len = wight.length;
let dp = new Array(len+1).fill(0).map(()=>new Array(size+1).fill(0))
for(let i = 1;i <= len;i++){
for(let j = 1; j <= size; j++){
if(wight[i-1] <= j){
dp[i][j] = Math.max(dp[i - 1][j],
value[i - 1] + dp[i - 1][j - wight[i - 1]])
}else {
dp[i][j] = dp[i-1][j]
}
}
}
return dp[len][size]
}
function test() {
console.log(testWeightBagProblem([1, 3, 4], [15, 20, 30], 4));
}
test();
416. 分割等和子集
给你一个 **只包含正整数** 的 **非空** 数组 `nums` 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
输入: nums = [1,5,11,5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11] 。
思路:先求出sum/2。就是0-1背包,不过他要求的事能否恰好装满。 dp[i][j]表示前i个物品能否恰好装满背包j
var canPartition = function(nums) {
const len = nums.length;
let sum = nums.reduce((pre,next)=>{return pre+next},0)
if(sum%2!==0){
return false
}
sum = sum / 2;
let dp = new Array(len + 1).fill(0).map(() => new Array(sum + 1).fill(false));
for (let i = 0; i <= len; i++) {
dp[i][0] = true
};
for (let i = 1; i <= len; i++) {//i从1开始遍历防止取dp[i - 1][j]的时候数组越界
let num = nums[i - 1]
//j从1开始,j为0的情况已经在dp数组初始化的时候完成了
for (let j = 1; j <= sum; j++) {
if (j - num < 0) {//背包容量不足 不能放入背包
dp[i][j] = dp[i - 1][j];//dp[i][j]取决于前i-1个物品是否能前好装满j的容量
} else {
//dp[i - 1][j]表示不装入第i个物品
//dp[i - 1][j-num]表示装入第i个,此时需要向前看前i - 1是否能装满j-num
//和背包的区别,这里只是返回true和false 表示能否装满,不用计算价值
dp[i][j] = dp[i - 1][j] || dp[i - 1][j - num];
}
}
}
return dp[len][sum]
};
198. 打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
dp[i]表示偷前i家能偷的最多钱, dp[i] = Math.max(dp[i-1],dp[i-2]+nums[i])
var rob = function(nums) {
let n = nums.length;
let dp = new Array(n).fill(0);
dp[0] = nums[0];
dp[1] = Math.max(nums[0],nums[1])
for(let i = 2;i < n; i++){
dp[i] = Math.max(dp[i-1],dp[i-2]+nums[i])
}
return dp[n-1]
};
64. 最小路径和
给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 说明:每次只能向下或者向右移动一步。
输入: grid = [[1,3,1],[1,5,1],[4,2,1]]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。
var minPathSum = function(grid) {
const n = grid.length;
const m = grid[0].length;
let dp = grid
for(let i = 1; i < n; i++){
dp[i][0] = dp[i][0] + dp[i-1][0]
}
for(let i = 1; i < m; i++){
dp[0][i] = dp[0][i] + dp[0][i-1]
}
for(let i = 1; i < n; i++){
for(let j = 1; j < m; j++){
dp[i][j] = dp[i][j] + Math.min(dp[i-1][j], dp[i][j-1])
}
}
return dp[n-1][m-1]
};