代码随想录算法训练营第三十二天(动态规划 一)

前几天有点忙加上贪心后面好难QWQ 暂时跳过两天的贪心,开始学动归

动态规划理论基础:

文章链接:代码随想录

文章思维导图:

文章摘要:

动态规划,英文:Dynamic Programming,简称DP,如果某一问题有很多重叠子问题,使用动态规划是最有效的。

动态规划的解题步骤(动归五部曲)

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

一些建议与解惑   

一些同学可能想为什么要先确定递推公式,然后在考虑初始化呢?

因为一些情况是递推公式决定了dp数组要如何初始化!

做动归找问题的最好方式就是把dp数组打印出来,看看究竟是不是按照自己思路推导的!

力扣题部分:

509. 斐波那契数

题目链接:. - 力扣(LeetCode)

题面:

斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:

F(0) = 0,F(1) = 1 F(n) = F(n - 1) + F(n - 2),其中 n > 1。

给定 n ,请计算 F(n) 。

思路:

动归五部曲:

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

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

2.确定递推公式

递推公式题目直接给我们了,我们可以写出状态转移方程 dp[i] = dp[i - 1] + dp[i - 2];

3.dp数组如何初始化

同样也是直接由题目得出dp[0] = 0, dp[1] = 1。

4.确定遍历顺序

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

5.举例推导dp数组

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

0 1 1 2 3 5 8 13 21 34 55

代码实现:

class Solution {
public:
    int fib(int n) {
        int dp[35];
        if(n == 0 || n == 1) return n;
        dp[1] = 1;
        for(int i = 2; i <= n; i ++)
        {
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        return dp[n];
    }
};

70. 爬楼梯

题目链接:. - 力扣(LeetCode)

题面:

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

思路:

动归五部曲:

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

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

2.确定递推公式

如何可以推出dp[i]呢?

首先是dp[i - 1],上i-1层楼梯,有dp[i - 1]种方法,那么再一步跳一个台阶不就是dp[i]了么。

还有就是dp[i - 2],上i-2层楼梯,有dp[i - 2]种方法,那么再一步跳两个台阶不就是dp[i]了么。

那么dp[i]就是 dp[i - 1]与dp[i - 2]之和!

递推公式: dp[n] = dp[n - 1] + dp[n - 2];

3.dp数组如何初始化

回顾一下dp[i]的定义:爬到第i层楼梯,有dp[i]种方法

递推公式需要两个值,由题目不难得出 dp[1] = 1, dp[2] = 2。

4.确定遍历顺序

遍历顺序一定是从前向后遍历的,和上一题本质其实一样。

5.举例推导dp数组

1 2 3 5 8 13 21 34 55

不少人肯定看出来了,这不就是斐波那契数列么!

和上一题区别就是没前两个数组而已。

代码实现:

class Solution {
public:
    int climbStairs(int n) {
        int dp[50];
        if(n == 1 || n == 2) return n;
        dp[1] = 1, dp[2] = 2;
        for(int i = 3; i <= n; i ++)
        {
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        return dp[n];
    }
};

746. 使用最小花费爬楼梯

题目链接:. - 力扣(LeetCode)

题面:

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。

你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。

请你计算并返回达到楼梯顶部的最低花费。

思路:

动归五部曲:

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

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

2.确定递推公式

可以有两个途径得到dp[i],一个是dp[i-1] 一个是dp[i-2]

dp[i - 1] 跳到 dp[i] 需要花费 dp[i - 1] + cost[i - 1]。

dp[i - 2] 跳到 dp[i] 需要花费 dp[i - 2] + cost[i - 2]。

那么究竟是选从dp[i - 1]跳还是从dp[i - 2]跳呢?

一定是选最小的,所以dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);

3.dp数组如何初始化

看一下递推公式,dp[i]由dp[i - 1],dp[i - 2]推出,既然初始化所有的dp[i]是不可能的,那么只初始化dp[0]和dp[1]就够了,其他的最终都是dp[0]dp[1]推出。

题目说你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。也就是说到达 第 0 个台阶是不花费的,但从 第0 个台阶往上跳的话,需要花费 cost[0]。

所以初始化 dp[0] = 0,dp[1] = 0;

4.确定遍历顺序

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

今天的三道题好像这个步骤不需要思考,但随着后面的学习,我们会遇到需要在这块做文章的题目的。

5.举例推导dp数组

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

代码实现:

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) 
    {
        int dp[1000] = {0};
        for(int i = 2; i < cost.size(); i ++)
        {
            dp[i] = min((dp[i - 1] + cost[i - 1]), (dp[i - 2] + cost[i - 2]));
            //cout<<dp[i]<<" ";
        }
        int ans = min(dp[cost.size() - 1] + cost[cost.size() - 1], dp[cost.size() - 2] + cost[cost.size() - 2]);
        return ans;
    }
};

  • 17
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值