首页 > 编程语言 >代码随想录算法Day38 | 动态规划理论基础 ,509. 斐波那契数 ,70. 爬楼梯 ,746. 使用最小花费爬楼梯

代码随想录算法Day38 | 动态规划理论基础 ,509. 斐波那契数 ,70. 爬楼梯 ,746. 使用最小花费爬楼梯

时间:2023-03-10 20:12:25浏览次数:47  
标签:契数 遍历 爬楼梯 int 随想录 cost 数组 dp

动态规划理论基础

动态规划五步曲:

  1. 确定dp数组(dp table)以及下标的含义
  2. 确定递推公式
  3. dp数组如何初始化
  4. 确定遍历顺序
  5. 举例推导dp数组

509. 斐波那契数

题目链接:509. 斐波那契数 - 力扣(LeetCode)

思路

  1. 确定dp数组以及下标的含义

dp[i]的定义为:第i个数的斐波那契数值是dp[i]

  1. 确定递推公式

状态转移方程 dp[i] = dp[i - 1] + dp[i - 2];

  1. dp数组如何初始化

dp[0] = 0;

dp[ 1 ] = 1;

  1. 确定遍历顺序

从递归公式dp[i] = dp[i - 1] + dp[i - 2];中可以看出,dp[i]是依赖 dp[i - 1] 和 dp[i - 2],那么遍历的顺序一定是从前到后遍历的

  1. 举例推导dp数组

按照这个递推公式dp[i] = dp[i - 1] + dp[i - 2],我们来推导一下,当N为10的时候,dp数组应该是如下的数列:

0 1 1 2 3 5 8 13 21 34 55

代码

 1 class Solution {
 2     public int fib(int n) {
 3         if (n < 2) return n;
 4         int a = 0, b = 1, c = 0;
 5         for (int i = 1; i < n; i++) {
 6             c = a + b;
 7             a = b;
 8             b = c;
 9         }
10         return c;
11     }
12 }
 1 //非压缩状态的版本
 2 class Solution {
 3     public int fib(int n) {
 4         if (n <= 1) return n;             
 5         int[] dp = new int[n + 1];
 6         dp[0] = 0;
 7         dp[1] = 1;
 8         for (int index = 2; index <= n; index++){
 9             dp[index] = dp[index - 1] + dp[index - 2];
10         }
11         return dp[n];
12     }
13 }

 

70. 爬楼梯

题目链接:70. 爬楼梯 - 力扣(LeetCode)

思路

在这道题中,可以明显看出 爬第一层有 1 种方法,爬第二层有 2 种方法。

到第三层时,由于可以爬 1 或 2 个台阶,所以可以在第一层基础上爬 2 个台阶到第三层,也可以在第二层的基础上爬 1 个台阶到第三层。

由此可见第三层楼梯的状态可以由第二层楼梯 和 到第一层楼梯状态推导出来,那么就可以想到动态规划了。

  1. 确定dp数组以及下标的含义

dp[i]: 爬到第i层楼梯,有dp[i]种方法

  1. 确定递推公式

dp[i] = dp[i - 1] + dp[i - 2] 。

在推导dp[i]的时候,一定要时刻想着dp[i]的定义,否则容易跑偏。

这体现出确定dp数组以及下标的含义的重要性!

  1. dp数组如何初始化

dp[1] = 1;

dp[2] = 2;

  1. 确定遍历顺序

从递推公式dp[i] = dp[i - 1] + dp[i - 2];中可以看出,遍历顺序一定是从前向后遍历的

  1. 举例推导dp数组

举例当n为5的时候,dp table(dp数组)应该是这样的

70.爬楼梯

代码

 1 // 常规方式
 2 public int climbStairs(int n) {
 3     int[] dp = new int[n + 1];
 4     dp[0] = 1;
 5     dp[1] = 1;
 6     for (int i = 2; i <= n; i++) {
 7         dp[i] = dp[i - 1] + dp[i - 2];
 8     }
 9     return dp[n];
10 }
 1 // 用变量记录代替数组
 2 class Solution {
 3     public int climbStairs(int n) {
 4         if(n <= 2) return n;
 5         int a = 1, b = 2, sum = 0;
 6 
 7         for(int i = 3; i <= n; i++){
 8             sum = a + b;  // f(i - 1) + f(i - 2)
 9             a = b;        // 记录f(i - 1),即下一轮的f(i - 2)
10             b = sum;      // 记录f(i),即下一轮的f(i - 1)
11         }
12         return b;
13     }
14 }

 

746. 使用最小花费爬楼梯

题目链接:746. 使用最小花费爬楼梯 - 力扣(LeetCode)

思路

 

  1. 确定dp数组以及下标的含义

dp[i]的定义:到达第i台阶所花费的最少体力为dp[i]。

  1. 确定递推公式

题目要求的是最少体力,所以dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);

  1. dp数组如何初始化

首先明白题意,跳到下标 0 或者 1 时不需要花费体力的,但从 0 或者 1 跳到其他层时需要花费当前下标对应的数字。所以:

 dp[0] = 0,dp[1] = 0;

  1. 确定遍历顺序

因为是模拟台阶,而且dp[i]由dp[i-1]dp[i-2]推出,所以是从前到后遍历cost数组就可以了。

  1. 举例推导dp数组

拿示例2:cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] ,来模拟一下dp数组的状态变化,如下:

代码

 1 // 方式一:第一步不支付费用
 2 class Solution {
 3     public int minCostClimbingStairs(int[] cost) {
 4         int len = cost.length;
 5         int[] dp = new int[len + 1];
 6 
 7         // 从下标为 0 或下标为 1 的台阶开始,因此支付费用为0
 8         dp[0] = 0;
 9         dp[1] = 0;
10 
11         // 计算到达每一层台阶的最小费用
12         for (int i = 2; i <= len; i++) {
13             dp[i] = Math.min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
14         }
15 
16         return dp[len];
17     }
18 }
 1 // 扩展
 2 // 方式二:第一步支付费用 最后一步不需要费用
 3 class Solution {
 4     public int minCostClimbingStairs(int[] cost) {
 5         int[] dp = new int[cost.length];
 6         dp[0] = cost[0];
 7         dp[1] = cost[1];
 8         for (int i = 2; i < cost.length; i++) {
 9             dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i];
10         }
11         //最后一步,如果是由倒数第二步爬,则最后一步的体力花费可以不用算
12         return Math.min(dp[cost.length - 1], dp[cost.length - 2]);
13     }
14 }

 

标签:契数,遍历,爬楼梯,int,随想录,cost,数组,dp
From: https://www.cnblogs.com/xpp3/p/17204549.html

相关文章