diff --git a/problems/0070.爬楼梯.md b/problems/0070.爬楼梯.md index 79b2d90b..fb23be7d 100644 --- a/problems/0070.爬楼梯.md +++ b/problems/0070.爬楼梯.md @@ -53,7 +53,7 @@ dp[i]: 爬到第i层楼梯,有dp[i]种方法 2. 确定递推公式 -如果可以推出dp[i]呢? +如何可以推出dp[i]呢? 从dp[i]的定义可以看出,dp[i] 可以有两个方向推出来。 @@ -73,7 +73,7 @@ dp[i]: 爬到第i层楼梯,有dp[i]种方法 在回顾一下dp[i]的定义:爬到第i层楼梯,有dp[i]中方法。 -那么i为0,dp[i]应该是多少呢,这个可以有很多解释,但都基本是直接奔着答案去解释的。 +那么i为0,dp[i]应该是多少呢,这个可以有很多解释,但基本都是直接奔着答案去解释的。 例如强行安慰自己爬到第0层,也有一种方法,什么都不做也就是一种方法即:dp[0] = 1,相当于直接站在楼顶。 @@ -91,7 +91,7 @@ dp[i]: 爬到第i层楼梯,有dp[i]种方法 我相信dp[1] = 1,dp[2] = 2,这个初始化大家应该都没有争议的。 -所以我的原则是:不考虑dp[0]如果初始化,只初始化dp[1] = 1,dp[2] = 2,然后从i = 3开始递推,这样才符合dp[i]的定义。 +所以我的原则是:不考虑dp[0]如何初始化,只初始化dp[1] = 1,dp[2] = 2,然后从i = 3开始递推,这样才符合dp[i]的定义。 4. 确定遍历顺序 @@ -163,7 +163,7 @@ public: 这道题目还可以继续深化,就是一步一个台阶,两个台阶,三个台阶,直到 m个台阶,有多少种方法爬到n阶楼顶。 -这又有难度了,这其实是一个完全背包问题,但力扣上没有这种题目,所以后续我在讲解背包问题的时候,今天这道题还会拿从背包问题的角度上来再讲一遍。 +这又有难度了,这其实是一个完全背包问题,但力扣上没有这种题目,所以后续我在讲解背包问题的时候,今天这道题还会从背包问题的角度上来再讲一遍。 这里我先给出我的实现代码: @@ -194,7 +194,7 @@ public: 这一连套问下来,候选人算法能力如何,面试官心里就有数了。 -**其实大厂面试最喜欢问题的就是这种简单题,然后慢慢变化,在小细节上考察候选人**。 +**其实大厂面试最喜欢的问题就是这种简单题,然后慢慢变化,在小细节上考察候选人**。 @@ -255,37 +255,37 @@ class Solution { class Solution: def climbStairs(self, n: int) -> int: # dp[i] 为第 i 阶楼梯有多少种方法爬到楼顶 - dp=[0]*(n+1) - dp[0]=1 - dp[1]=1 - for i in range(2,n+1): - dp[i]=dp[i-1]+dp[i-2] + dp = [0]*(n+1) + dp[0] = 1 + dp[1] = 1 + for i in range(2, n+1): + dp[i] = dp[i-1] + dp[i-2] return dp[n] # 空间复杂度为O(1)版本 class Solution: def climbStairs(self, n: int) -> int: - dp=[0]*(n+1) - dp[0]=1 - dp[1]=1 + dp = [0]*(n+1) + dp[0] = 1 + dp[1] = 1 for i in range(2,n+1): - tmp=dp[0]+dp[1] - dp[0]=dp[1] - dp[1]=tmp + tmp = dp[0] + dp[1] + dp[0] = dp[1] + dp[1] = tmp return dp[1] ``` ### Go ```Go func climbStairs(n int) int { - if n==1{ + if n == 1 { return 1 } - dp:=make([]int,n+1) - dp[1]=1 - dp[2]=2 - for i:=3;i<=n;i++{ - dp[i]=dp[i-1]+dp[i-2] + dp := make([]int, n+1) + dp[1] = 1 + dp[2] = 2 + for i := 3; i <= n; i++ { + dp[i] = dp[i-1] + dp[i-2] } return dp[n] } diff --git a/problems/0343.整数拆分.md b/problems/0343.整数拆分.md index 8a5e9b8f..25a63154 100644 --- a/problems/0343.整数拆分.md +++ b/problems/0343.整数拆分.md @@ -35,7 +35,7 @@ dp[i]:分拆数字i,可以得到的最大乘积为dp[i]。 -dp[i]的定义讲贯彻整个解题过程,下面哪一步想不懂了,就想想dp[i]究竟表示的是啥! +dp[i]的定义将贯彻整个解题过程,下面哪一步想不懂了,就想想dp[i]究竟表示的是啥! 2. 确定递推公式 @@ -93,7 +93,7 @@ for (int i = 3; i <= n ; i++) { } } ``` -注意 枚举j的时候,是从1开始的。从0开始的话,那么让拆分一个数拆个0的话,那么求最大乘积没有意义了。 +注意 枚举j的时候,是从1开始的。从0开始的话,那么让拆分一个数拆个0,求最大乘积就没有意义了。 j的结束条件是 j < i - 1 ,其实 j < i 也是可以的,不过可以节省一步,例如让j = i - 1,的话,其实在 j = 1的时候,这一步就已经拆出来了,重复计算,所以 j < i - 1 @@ -109,15 +109,15 @@ for (int i = 3; i <= n ; i++) { } ``` -因为拆分一个数n 使之乘积最大,那么一定是拆分m个成近似相同的子数相乘才是最大的。 +因为拆分一个数n 使之乘积最大,那么一定是拆分成m个近似相同的子数相乘才是最大的。 例如 6 拆成 3 * 3, 10 拆成 3 * 3 * 4。 100的话 也是拆成m个近似数组的子数 相乘才是最大的。 -只不过我们不知道m究竟是多少而已,但可以明确的是m一定大于等于2,既然m大于等于,也就是 最差也应该是拆成两个相同的 可能是最大值。 +只不过我们不知道m究竟是多少而已,但可以明确的是m一定大于等于2,既然m大于等于2,也就是 最差也应该是拆成两个相同的 可能是最大值。 那么 j 遍历,只需要遍历到 n/2 就可以,后面就没有必要遍历了,一定不是最大值。 -至于 “拆分一个数n 使之乘积最大,那么一定是拆分m个成近似相同的子数相乘才是最大的” 这个我就不去做数学证明了,感兴趣的同学,可以自己证明。 +至于 “拆分一个数n 使之乘积最大,那么一定是拆分成m个近似相同的子数相乘才是最大的” 这个我就不去做数学证明了,感兴趣的同学,可以自己证明。 5. 举例推导dp数组 @@ -221,14 +221,14 @@ public: class Solution { public int integerBreak(int n) { //dp[i] 为正整数 i 拆分后的结果的最大乘积 - int[]dp=new int[n+1]; - dp[2]=1; - for(int i=3;i<=n;i++){ - for(int j=1;j<=i-j;j++){ + int[] dp = new int[n+1]; + dp[2] = 1; + for(int i = 3; i <= n; i++) { + for(int j = 1; j <= i-j; j++) { // 这里的 j 其实最大值为 i-j,再大只不过是重复而已, //并且,在本题中,我们分析 dp[0], dp[1]都是无意义的, //j 最大到 i-j,就不会用到 dp[0]与dp[1] - dp[i]=Math.max(dp[i],Math.max(j*(i-j),j*dp[i-j])); + dp[i] = Math.max(dp[i], Math.max(j*(i-j), j*dp[i-j])); // j * (i - j) 是单纯的把整数 i 拆分为两个数 也就是 i,i-j ,再相乘 //而j * dp[i - j]是将 i 拆分成两个以及两个以上的个数,再相乘。 } @@ -254,7 +254,7 @@ class Solution: ``` ### Go -```golang +```go func integerBreak(n int) int { /** 动态五部曲 @@ -264,40 +264,25 @@ func integerBreak(n int) int { 4.确定遍历顺序 5.打印dp **/ - dp:=make([]int,n+1) - dp[1]=1 - dp[2]=1 - for i:=3;ib{ +func max(a, b int) int{ + if a > b { return a } return b } ``` -### Rust -```rust -pub fn integer_break(n: i32) -> i32 { - let n = n as usize; - let mut dp = vec![0; n + 1]; - dp[2] = 1; - for i in 3..=n { - for j in 1..i-1 { - dp[i] = dp[i].max((i - j) * j).max(dp[i - j] * j); - } - } - dp[n] as i32 -} -``` - ### Javascript ```Javascript var integerBreak = function(n) { @@ -313,6 +298,21 @@ var integerBreak = function(n) { }; ``` +### Rust +```rust +pub fn integer_break(n: i32) -> i32 { + let n = n as usize; + let mut dp = vec![0; n + 1]; + dp[2] = 1; + for i in 3..=n { + for j in 1..i-1 { + dp[i] = dp[i].max((i - j) * j).max(dp[i - j] * j); + } + } + dp[n] as i32 +} +``` + ### TypeScript ```typescript diff --git a/problems/0746.使用最小花费爬楼梯.md b/problems/0746.使用最小花费爬楼梯.md index b6f5a734..6de0f8ec 100644 --- a/problems/0746.使用最小花费爬楼梯.md +++ b/problems/0746.使用最小花费爬楼梯.md @@ -83,7 +83,7 @@ dp[i - 2] 跳到 dp[i] 需要花费 dp[i - 2] + cost[i - 2]。 那么 dp[0] 应该是多少呢? 根据dp数组的定义,到达第0台阶所花费的最小体力为dp[0],那么有同学可能想,那dp[0] 应该是 cost[0],例如 cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] 的话,dp[0] 就是 cost[0] 应该是1。 -这里就要说名了,本题力扣为什么改题意了,而且修改题意之后 就清晰很多的原因了。 +这里就要说明本题力扣为什么改题意,而且修改题意之后 就清晰很多的原因了。 新题目描述中明确说了 “你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。” 也就是说 从 到达 第 0 个台阶是不花费的,但从 第0 个台阶 往上跳的话,需要花费 cost[0]。 @@ -101,7 +101,7 @@ dp[i - 2] 跳到 dp[i] 需要花费 dp[i - 2] + cost[i - 2]。 > **但是稍稍有点难度的动态规划,其遍历顺序并不容易确定下来**。 > 例如:01背包,都知道两个for循环,一个for遍历物品嵌套一个for遍历背包容量,那么为什么不是一个for遍历背包容量嵌套一个for遍历物品呢? 以及在使用一维dp数组的时候遍历背包容量为什么要倒序呢? -**这些都是遍历顺序息息相关。当然背包问题后续「代码随想录」都会重点讲解的!** +**这些都与遍历顺序息息相关。当然背包问题后续「代码随想录」都会重点讲解的!** 5. 举例推导dp数组 @@ -182,7 +182,7 @@ public: ## 总结 -大家可以发现这道题目相对于 昨天的[动态规划:爬楼梯](https://programmercarl.com/0070.爬楼梯.html)有难了一点,但整体思路是一样。 +大家可以发现这道题目相对于 昨天的[动态规划:爬楼梯](https://programmercarl.com/0070.爬楼梯.html)又难了一点,但整体思路是一样的。 从[动态规划:斐波那契数](https://programmercarl.com/0509.斐波那契数.html)到 [动态规划:爬楼梯](https://programmercarl.com/0070.爬楼梯.html)再到今天这道题目,录友们感受到循序渐进的梯度了嘛。 @@ -243,43 +243,43 @@ class Solution { ```python class Solution: def minCostClimbingStairs(self, cost: List[int]) -> int: - dp = [0] * (len(cost)) - dp[0] = cost[0] - dp[1] = cost[1] - for i in range(2, len(cost)): - dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i] - return min(dp[len(cost) - 1], dp[len(cost) - 2]) + dp = [0] * (len(cost) + 1) + dp[0] = 0 + dp[1] = 0 + for i in range(2, len(cost) + 1): + dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i-2]) + return dp[len(cost)] ``` ### Go ```Go func minCostClimbingStairs(cost []int) int { - dp := make([]int, len(cost)) - dp[0], dp[1] = cost[0], cost[1] - for i := 2; i < len(cost); i++ { - dp[i] = min(dp[i-1], dp[i-2]) + cost[i] - } - return min(dp[len(cost)-1], dp[len(cost)-2]) + f := make([]int, len(cost) + 1) + f[0], f[1] = 0, 0 + for i := 2; i <= len(cost); i++ { + f[i] = min(f[i-1] + cost[i-1], f[i-2] + cost[i-2]) + } + return f[len(cost)] } - func min(a, b int) int { - if a < b { - return a - } - return b + if a < b { + return a + } + return b } ``` ### Javascript ```Javascript var minCostClimbingStairs = function(cost) { - const dp = [ cost[0], cost[1] ] - - for (let i = 2; i < cost.length; ++i) { - dp[i] = Math.min(dp[i -1] + cost[i], dp[i - 2] + cost[i]) + const n = cost.length; + const dp = new Array(n + 1); + dp[0] = dp[1] = 0; + for (let i = 2; i <= n; ++i) { + dp[i] = Math.min(dp[i -1] + cost[i - 1], dp[i - 2] + cost[i - 2]) } - return Math.min(dp[cost.length - 1], dp[cost.length - 2]) + return dp[n] }; ``` @@ -289,19 +289,19 @@ var minCostClimbingStairs = function(cost) { function minCostClimbingStairs(cost: number[]): number { /** dp[i]: 走到第i阶需要花费的最少金钱 - dp[0]: cost[0]; - dp[1]: cost[1]; + dp[0]: 0; + dp[1]: 0; ... - dp[i]: min(dp[i - 1], dp[i - 2]) + cost[i]; + dp[i]: min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]); */ - const dp: number[] = []; - const length: number = cost.length; - dp[0] = cost[0]; - dp[1] = cost[1]; + const dp = []; + const length = cost.length; + dp[0] = 0; + dp[1] = 0; for (let i = 2; i <= length; i++) { - dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i]; + dp[i] = Math.min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]); } - return Math.min(dp[length - 1], dp[length - 2]); + return dp[length]; }; ```