算法设计思想——动态规划

文章介绍了动态规划的概念,强调了其解决重叠子问题和具有最优子结构的问题的特点。通过爬楼梯问题为例,详细阐述了动态规划的五步法,包括定义状态、确定递推公式、初始化、计算状态和求解原问题,并提供了两种不同的代码实现。文章最后讨论了时间复杂度和空间复杂度,并指出这个问题实际上与斐波那契数列相关。
摘要由CSDN通过智能技术生成

动态规划Dynamic Programming,简称DP)

是一种常见的算法设计方法,用于解决一类重叠子问题的优化问题。他的基本思想是将问题分解成多个重叠的子问题,递归求解,并将子问题的求解缓存起来,避免重复计算,从而得到问题的解。

动态规划通常适用于以下两个条件的问题:
1.重叠子问题:原问题可以分解为若干个子问题,子问题之间存在重叠部分,即某些问题的解在求解过程中被多次引用。

2.最优子结构:原问题的最优解可以由子问题的最优解推导得到,即原问题的最优解包含子问题的最优解。

动态规划算法的基本步骤如下:

  1. 定义状态:将原问题划分为若干个子问题,并定义状态表示子问题的解。
  2. 定义状态转移方程:根据子问题之间的关系,建立子问题之间的递推关系式,即状态转移方程。
  3. 初始化状态:确定初始化的值,即最简单的子问题的解。
  4. 计算状态:按照状态转移方程,从简单的子问题开始递推,计算出所有子问题的解。
  5. 求解原问题:根据子问题的解,求出原问题的解 。

动态规划算法的时间复杂度通常为O\left ( n^{2} \right )O\left ( n^{3} \right ),取决于问题的规模和状态转移方程的复杂度。

下面来看例题:

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

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

下面以动态规划五部曲原则来进行构思:

  1. 定义状态:需要定义一个一维数组来记录不同楼层的状态,因此我们需要——确定St数组以及下标的含义        St[i]:爬到第i层,有St[i]种方法
  2. 确定递推公式:首先如果是 st[i-1],上 i-1 层楼梯,有 st[i-1] 种方法,那么再一步跳一个台阶就是 St[i];如果是 St[i-2],上 i-2 层楼梯,有 St[i-2] 种方法,那么再跳一步两个台阶就是St[i],于是st[i] = st[i-1] + st[i-2]。
  3. St数组的初始化:我们需要考虑为St为0的情况,按照题干来理解,当楼层为0的时候,我们什么也不用做,于是i就为 0。有一个争议点就是:当St[0]应该为1的理由其实是因为dp[0] =1的话在递推的过程中 i 从 2 开始遍历才能执行下去,实际从题干出发,n是正整数,那么i也应该为为正整数,所以不需要讨论St[0]的初始化,我们直接从dp[1] = 1;dp[2] =2,然后从 i = 3开始递推。
  4. 确定遍历顺序:由递推公式st[i] = st[i-1] + st[i-2]得到,遍历顺序从前向后
  5. 举例推导St数组:当例如,我们n = 6时,st table如下:

没错,这其实就是斐波那契数列(Fibonacci sequence),五部曲分析完成后,我们的代码如下:

#include <iostream>
#include <vector>
using namespace std;

class MyMethod {
public:
    int clbStairs(int n){
        if (n == 1) return 1;
        if (n == 2) return 2;
        vector<int> st(n+1);
        st[1] = 1;
        st[2] = 2;
        for (int i = 3; i <= n; i++){
            st[i] = st[i-1] + st[i-2];
        }
        return st[n];
    }
};

int main(){
    int n = 6;
    int ret = MyMethod().clbStairs(n);
    cout << ret << endl;
    return 0;
}

 时间复杂度(time complexity):O\left ( n \right )

空间复杂度(space complexity):O(n)

考虑到代码优化的话,我们用sum减少数据结构,使用常量来保存状态,代码如下:

#include <iostream>
#include <vector>
using namespace std;

class MyMethod {
public:
    int clbStairs(int n){
        if (n == 1) return 1;
        if (n == 2) return 2;
        vector<int> st(n+1);
        st[1] = 1;
        st[2] = 2;
        for (int i = 3; i <= n; i++){
            int sum = st[1] + st[2];
            st[1] = st[2];
            st[2] = sum;
        }
        return st[2];
    }
};

int main(){
    int n = 6;
    int ret = MyMethod().clbStairs(n);
    cout << ret << endl;
    return 0;
}

 时间复杂度(time complexity):O\left ( n \right )

空间复杂度(space complexity):O\left ( 1 \right )

总结

虽然这道题的实质是斐波那契数列,但理解到动态规划的程序设计思路其实没那么轻松,关键是能够迅速捕捉到这一概念,进行建模,按照动态规划五部曲的递推公式,逐步推导得到结果。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值