【算法题】动态规划基础阶段之 爬楼梯 和 杨辉三角

前言

动态规划(Dynamic Programming,简称 DP)是一种解决多阶段决策过程最优化问题的方法。它是一种将复杂问题分解成重叠子问题的策略,通过维护每个子问题的最优解来推导出问题的最优解。

动态规划的主要思想是利用已求解的子问题的最优解来推导出更大问题的最优解,从而避免了重复计算。因此,动态规划通常采用自底向上的方式进行求解,先求解出小规模的问题,然后逐步推导出更大规模的问题,直到求解出整个问题的最优解。

动态规划通常包括以下几个基本步骤:

  1. 定义状态:将问题划分为若干个子问题,并定义状态表示子问题的解;
  2. 定义状态转移方程:根据子问题之间的关系,设计状态转移方程,即如何从已知状态推导出未知状态的计算过程;
  3. 确定初始状态:定义最小的子问题的解;
  4. 自底向上求解:按照状态转移方程,计算出所有状态的最优解;
  5. 根据最优解构造问题的解。

动态规划可以解决许多实际问题,例如最短路径问题、背包问题、最长公共子序列问题、编辑距离问题等。同时,动态规划也是许多其他算法的核心思想,例如分治算法、贪心算法等。

动态规划是一种解决多阶段决策过程最优化问题的方法,它将复杂问题分解成重叠子问题,通过维护每个子问题的最优解来推导出问题的最优解。动态规划包括定义状态、设计状态转移方程、确定初始状态、自底向上求解和构造问题解等步骤。动态规划可以解决许多实际问题,也是其他算法的核心思想之一。

二、爬楼梯

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

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

示例 1:

输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。

  1. 1 阶 + 1 阶
  2. 2 阶

示例 2:

输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。

  1. 1 阶 + 1 阶 + 1 阶
  2. 1 阶 + 2 阶
  3. 2 阶 + 1 阶

来源:力扣(LeetCode)。

2.1、思路

本问题其实常规解法可以分成多个子问题,爬第n阶楼梯的方法数量,等于 2 部分之和:

  1. 爬上n−1 阶楼梯的方法数量。因为再爬1阶就能到第n阶。
  2. 爬上n−2 阶楼梯的方法数量,因为再爬2阶就能到第n阶。

所以我们得到公式 :
dp[n]=dp[n−1]+dp[n−2]
同时需要初始化 dp[0]=1 和 dp[1]=1。
时间复杂度:O(n)。
在这里插入图片描述

2.2、代码实现

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

    }
};

上述代码动态分配一个数组,造成算法的空间复杂度为O(N),由于每次只要最近的两次结果,我们可以只保存最近的两次结果,而不保存所有的结果,即滚动数组。优化代码如下:

class Solution {
public:
    int climbStairs(int n) {
        int pre1=1,pre2=1,cur=1;
        for(int i=2;i<=n;i++)
        {
            cur=pre1+pre2;
            pre1=pre2;
            pre2=cur;
        }
        return cur;

    }
};

三、杨辉三角

给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。

在「杨辉三角」中,每个数是它左上方和右上方的数的和。

在这里插入图片描述

示例 1:

输入: numRows = 5
输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]

示例 2:

输入: numRows = 1
输出: [[1]]

来源:力扣(LeetCode)。

3.1、思路

从第三行开始,除了开始和末尾的位置上的元素,其余位置上的元素都是由上方的元素以及上方左侧的元素相加得到的,此时就很容易的到从第三行开始状态转移方程为dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1],之后就可轻松求解。

3.2、代码实现

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> res(numRows);
        for(int i=0;i<numRows;i++)
        {
            res[i].resize(i+1);
            res[i][0]=res[i][i]=1;
            for(int j=1;j<i;j++)
            {
                res[i][j]=res[i-1][j-1]+res[i-1][j];
            }

        }
        return res;
    }
};

时间复杂度: O ( n u m R o w s 2 ) O(numRows^2) O(numRows2)

空间复杂度:O(1);不考虑返回值的空间占用。

四、杨辉三角2(进阶)

给定一个非负索引 rowIndex,返回「杨辉三角」的第 rowIndex 行。

在「杨辉三角」中,每个数是它左上方和右上方的数的和。
在这里插入图片描述
示例 1:

输入: rowIndex = 3
输出: [1,3,3,1]

示例 2:

输入: rowIndex = 0
输出: [1]

示例 3:

输入: rowIndex = 1
输出: [1,1]

来源:力扣(LeetCode)。

思路:递推的方式,保存杨辉三角的所有结果,只返回需要的那行数组。

代码实现:

class Solution {
public:
    vector<int> getRow(int rowIndex) {
        vector<vector<int>> res(rowIndex+1);
        for(int i=0;i<=rowIndex;i++)
        {
            res[i].resize(i+1);
            res[i][0]=res[i][i]=1;
            for(int j=1;j<i;j++)
            {
                res[i][j]=res[i-1][j-1]+res[i-1][j];
            }
        }
        return res[rowIndex];
    }
};

注意到对第 i+1 行的计算仅用到了第 i 行的数据,因此可以使用滚动数组的思想优化空间复杂度。

class Solution {
public:
    vector<int> getRow(int rowIndex) {
        vector<int> pre, cur;
        for (int i = 0; i <= rowIndex; ++i) {
            cur.resize(i + 1);
            cur[0] = cur[i] = 1;
            for (int j = 1; j < i; ++j) {
                cur[j] = pre[j - 1] + pre[j];
            }
            pre = cur;
        }
        return pre;
    }
};

时间复杂度: O ( r o w I n d e x 2 ) O(rowIndex^2) O(rowIndex2)

空间复杂度:O(1);不考虑返回值的空间占用。

总结

动态规划(Dynamic Programming)是一种解决多阶段决策最优化问题的方法,它将复杂问题分解成重叠子问题并通过维护每个子问题的最优解来推导出问题的最优解。动态规划可以解决许多实际问题,例如最短路径问题、背包问题、最长公共子序列问题、编辑距离问题等。

动态规划的基本思想是利用已求解的子问题的最优解来推导出更大问题的最优解,从而避免了重复计算。它通常采用自底向上的方式进行求解,先求解出小规模的问题,然后逐步推导出更大规模的问题,直到求解出整个问题的最优解。

动态规划通常包括以下几个基本步骤:

  1. 定义状态:将问题划分为若干个子问题,并定义状态表示子问题的解;
  2. 定义状态转移方程:根据子问题之间的关系,设计状态转移方程,即如何从已知状态推导出未知状态的计算过程;
  3. 确定初始状态:定义最小的子问题的解;
  4. 自底向上求解:按照状态转移方程,计算出所有状态的最优解;
  5. 根据最优解构造问题的解。

动态规划的时间复杂度通常为 O ( n 2 ) O(n^2) O(n2) O ( n 3 ) O(n^3) O(n3),空间复杂度为O(n),其中n表示问题规模。在实际应用中,为了减少空间复杂度,通常可以使用滚动数组等技巧来优化动态规划算法。

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是关于算法设计与分析杨辉三角的多种解法: 1. 使用列表存储每一行的值,利用上一行的值计算当前行的值,然后将每一行的列表打印出来。 ```python def generate_pascals_triangle(numRows): triangle = [] for row_num in range(numRows): row = [None for _ in range(row_num + 1)] row[0], row[-1] = 1, 1 for j in range(1, len(row) - 1): row[j] = triangle[row_num - 1][j - 1] + triangle[row_num - 1][j] triangle.append(row) return triangle def print_pascals_triangle(triangle): for row in triangle: print(row) numRows = 5 triangle = generate_pascals_triangle(numRows) print_pascals_triangle(triangle) ``` 2. 使用生成器函数生成每一行的值,然后打印出整个杨辉三角。 ```python def generate_pascals_triangle(numRows): result = [] for i in range(numRows): row = [1] * (i + 1) for j in range(1, i): row[j] = result[i - 1][j - 1] + result[i - 1][j] result.append(row) return result def print_pascals_triangle(triangle): for row in triangle: print(row) numRows = 5 triangle = generate_pascals_triangle(numRows) print_pascals_triangle(triangle) ``` 3. 使用递归函数计算每一行的值,然后打印出整个杨辉三角。 ```python def generate_pascals_triangle(numRows): if numRows == 0: return [] elif numRows == 1: return [[1]] else: result = generate_pascals_triangle(numRows - 1) last_row = result[-1] new_row = [1] + [last_row[i] + last_row[i + 1] for i in range(len(last_row) - 1)] + [1] result.append(new_row) return result def print_pascals_triangle(triangle): for row in triangle: print(row) numRows = 5 triangle = generate_pascals_triangle(numRows) print_pascals_triangle(triangle) ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lion Long

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值