70. 爬楼梯
链接
文章链接
题目链接
第一想法
之前那贪心算法写过了,这次尝试用动态规划,还是递归五部曲
- dp数组的含义,dp[i]为爬到台阶为i的方法数
- 初始化,其他的都为0,只有dp[0]=1,原因是让递推公式可以顺利递推,防止递推公式的结果都是0
- 递推公式,arr[j] += arr[j - i] 这里的i为物品(也就是一次能走几步)
- 遍历顺序 还是使用的是先物品再背包(因为先迈1再迈2和先迈2再迈1是不同的)
function climbStairs(n: number): number {
let arr: number[] = new Array(n + 1).fill(0)
arr[0] = 1
for (let j = 0; j <= n; j++) {
for (let i = 1; i <= 2; i++) {
if (j >= i) arr[j] += arr[j - i]
}
}
return arr[n]
}
看完文字的想法
文章的想法和我是一致的,这里就赘述了
思考
这道题我还是认为贪心算法比较简单,但是如果一次可以迈m步的话(m>=3),那么动态规划还是比贪心好写的,原因就是按照动态规划的解法把2改成m就可以了
322. 零钱兑换
链接
文章链接
题目链接
第一想法
这道题说实话第一眼看到是没有思路的,但是想了一下发现是可以的,因为是要求和为amount的最少硬币数,所以dp数组的含义:dp[j]为和为j的最少硬币数为dp[j]。接下来初始化,这次初始化就不可以为0了,因为是最小硬币数,所以取min的话0是最小的,所以取的是最大的数,其次,由例子可知dp[0]=0. 接下来递推公式为 dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1)
含义为不使用当前硬币的数量和使用当前硬币的数量之间的最小值。
最后,遍历顺序肯定先为物品再为背包,因为是组合问题,和排列无关。代码如下:
function coinChange(coins: number[], amount: number): number {
let dp: number[] = new Array(amount + 1).fill(Number.MAX_SAFE_INTEGER)
dp[0] = 0
for (let i = 0; i < coins.length; i++) {
for (let j = coins[i]; j <= amount; j++) {
dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1)
}
}
return dp[amount] == Number.MAX_SAFE_INTEGER ? -1 : dp[amount]
}
看完文章后的想法
直接上一张图来模拟解法:
文章的整体思路是和我的是一样的,但是我自己的想法是有点问题的,这个既不是组合问题也不是排列问题,所以用排列或者组合都是可以的。
思考
这道题不算太难,如果能把dp数组想明白是什么意思就可以了,把dp数组想明白了。初始化也就出来了。递推公式也就不难推导出来了,这道题先遍历背包和先遍历物品都是一样的,因为这个不是组合和排列问题,我的第一想法关于这个是有错误的。
279. 完全平方数
链接
文章链接
题目链接
第一想法
这道题和322. 零钱兑换这道题是类似的,都是求和为n的最少组合数,那么接着递归五部曲:
- dp数组的含义,dp[j]为和为j的最少组合数
- 初始化,和零钱兑换一样的,都是最大值,但是dp[0]还是为0的
- 递推公式为
dp[j] = Math.min(dp[j], dp[j - i ** 2] + 1)
,比较算上当前平方最小还是不算上当前平法的组合数最小 - 遍历顺序,因为和组合还是排列无关,所以先物品还是先背包都是可以的
代码如下:
function numSquares(n: number): number {
let dp: number[] = new Array(n + 1).fill(Infinity)//dp数组
dp[0] = 0//初始化
let curr: number = Math.floor(Math.sqrt(n))//这里是做了个优化,因为求组合数最少为1 就是当前数正好能被开平方
for (let i = 1; i <= curr; i++) {
for (let j = i ** 2; j <= n; j++) {//这里j从i的平方开始算的原因是,每进行一次递归,要用到的是当前数的平方,所以进行运算的话也应该和平方有关
dp[j] = Math.min(dp[j], dp[j - i ** 2] + 1)
}
}
return dp[n]
}
看完文章后的想法
文章的想法和我的是一样的,这里就不多说了,这里给出先背包再物品的解法:
function numSquares(n: number): number {
let dp: number[] = new Array(n + 1).fill(Infinity)//dp数组
dp[0] = 0//初始化
for (let j = 1; j <= n; j++) {//背包
for (let i = 1; i * i <= j; i++) {//物品
dp[j] = Math.min(dp[j], dp[j - i * i] + 1)
}
}
return dp[n]
}
思考
这道题由于之前写过零钱兑换,所以再写这道题是很简单的,这道题还是先背包和先物品都是可以的,但是文章的写法比我自己的更加好理解,我那个也不算属于优化,只是没想到文章那种简便的写法罢了。
今日总结
今日3道题,耗时3小时