【进击的算法】基础算法——动态规划

在这里插入图片描述

本文主题:动态规划
更多算法:回溯算法
我的主页:蓝色学者的主页

文章目录

  • 一、前言
  • 二、概念
    • 2.1概念一:状态转移
    • 2.2概念二:Dp数组
  • 三、例题
    • 3.1斐波那契数列
      • 3.1.1题目描述
      • 3.1.2状态转移
      • 3.1.3Dp数组
        • Dp数组优化:
      • 参考代码
    • 3.2爬楼梯
      • 3.2.1 题目描述
      • 3.2.2状态转移
      • 3.2.3Dp数组
      • 参考代码
  • 四、作业
  • 五、结语

一、前言

很开心又和大家见面了,今天我们来一起学习一下传说中的动态规划,通过两道很经典的动态规划题目,帮助大家快速的理解动态规划的思想(斐波那契数列、爬楼梯),之后我还会留下本节课的作业,感兴趣的话一起来看看吧~

二、概念

动态规划(Dynamic Programming)是运筹学的一个分支,是求解决策过程最优化的过程。在初次见到动态规划的题目时,我们会听到诸如:状态转移方程、Dp数组等概念,我们就从这些概念入手,揭开动态规划的神秘面纱~

2.1概念一:状态转移

何为状态转移呢?我想让你思考一个问题:

求4!= ?
注:[n!]指的是n的阶乘,例如 3!= 3 * 2 * 1

根据阶乘的定义,我们想到 4!= 4 * 3 * 2 * 1 = 24
再仔细观察一下,就会得出 4!= 4 * 3!

这样我们得出了一个结论:

要求[n]的阶乘,只需要求出[n-1]的阶乘,要求[n-1]的阶乘,只需要求出[n-2]的阶乘

这样我们就完成了一次状态转移,即:要求[n]的值,先去求[n-1]的值

2.2概念二:Dp数组

理解了状态转移,我们再来看看Dp数组是什么,同样的,我们再思考一个问题:

为什么要有Dp数组?

不妨再去思考一下上面阶乘的问题,如果要求[4!],我们需要什么呢?
要求[4!]——需要先求[3!]
要求[3!]——需要先求[2!]
要求[2!]——需要先求[1!]

假如没有一个Dp数组,我们将之前的阶乘结果放到哪里去呢!
在计算 [4!] 的时候,希望能够直接拿到 [3!],因此我们必须要把 [3!] 实现存入一个数组。

我们把这种存放前面状态的数组,叫做Dp数组,正是有这种数组帮助,我们才能省去很多繁琐的重复计算

三、例题

3.1斐波那契数列

3.1.1题目描述

题目链接:剑指offer-斐波那契数列
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。

斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.

3.1.2状态转移

根据本题对斐波那契数列的定义,我们知道第n项等于第n-1项加上第n-2项,与状态转移概念相符,因此本题我们可以使用动态规划来求解。
【进击的算法】基础算法——动态规划_第1张图片

3.1.3Dp数组

要求第n个斐波那契数,就要求第n-1个和第n-2的斐波那契数,因此我们的Dp数组就应该存放斐波那契数,注意,由于Dp(0)、Dp(1)没有前两个斐波那契数,因此我们要对这两个元素提前赋值。

Dp[0] = 0;
Dp[1] = 1;

Dp数组优化:

仔细观察状态转移,我们会发现与阶乘还不同的是,斐波那契数只依赖于前两个斐波那契数,因此我们可以将Dp数组优化成两个变量。

注:两者本质上都是动态规划的思路

参考代码

纯动态规划代码:

int fib(int n)
{
    if(n == 0) return 0;
    if(n == 1) return 1;
    int Dp[n+1];
    Dp[0] = 0;
    Dp[1] = 1;
    for(int i = 2;i<=n;i++)
        Dp[i] = (Dp[i-1] + Dp[i-2]) % 1000000007;
    return Dp[n];
}

Dp数组优化代码:

int fib(int n)
{
    if(n == 0) return 0;
    if(n == 1) return 1;
    int F1 = 0;
    int F2 = 1;
    int F3 = F1 + F2;
    for(int i = 2;i<n;i++)
    {
        F1 = F2;
        F2 = F3;
        F3 = (F1 + F2)%1000000007;
    }
    return F3;
}

3.2爬楼梯

3.2.1 题目描述

题目链接:leetcode70 爬楼梯
假设你正在爬楼梯。需要 [n] 阶你才能到达楼顶。
每次你可以爬 [1] 或 [2] 个台阶。你有多少种不同的方法可以爬到楼顶呢?

3.2.2状态转移

根据题目要求,要爬到第n楼,有两种方法,也就是从[n-1]楼爬1步或者从[n-2]楼爬两步,我们肯定不是从底层直接到[n-1]、[n-2]楼的,那么这个问题就转换成了有多少种方法到[n-1]+有多少方法到[n-2]的一个问题了,这样我们就完成了状态转移的分析

3.2.3Dp数组

要求到第[n]楼要爬多少台阶,Dp数组就是存放要爬多少台阶的数组,由于第一楼、第二楼没有前两楼,我们需要对其初始化:

Dp[1] = 1;
Dp[2] = 1;

参考代码

int climbStairs(int n){
    //dp数组,记录 有几种方法爬到第 n 阶段
    if(n == 1) return 1;
    if(n == 2) return 2;
    int* dp = (int*)malloc(sizeof(int)*(n+1));
    dp[1] = 1;
    dp[2] = 2;
    for(int i = 3;i<=n;i++)
    {
        dp[i] = dp[i-2] + dp[i-1];
    }

    int methed = dp[n];
    return methed;
}

四、作业

分析完两道很经典的动态规划的例题,相信你一定对动态规划有了初步的了解,可以通过下面两道题巩固知识

如果你还想练习一道基础题目:使用最小花费爬楼梯
如果你要挑战稍难一点的题目:不同路径

五、结语

如果你感觉有所收获,可以点赞 + 收藏 +关注 支持一下学者哦~ 我们下次见~

你可能感兴趣的:(进击的算法,算法,动态规划)