〖动态规划60题〗泰波纳契数列模型

这篇具有很好参考价值的文章主要介绍了〖动态规划60题〗泰波纳契数列模型。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.第N个泰波那契数(简单)

  • 题目链接:第N个泰波那契数

  • 题目描述

泰波那契序列 Tn 定义如下:
T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2
给你整数 n,请返回第 n 个泰波那契数 Tn 的值。

解题流程

1. 状态表示

在解任何一道动态规划题目时,我们都需要先给出一张dp表,用来存储某种状态。

  • dp[i]:在本题目中,dp[i]表示的含义是第i个泰波纳契数的值

2. 状态转移方程

所谓状态转移方程就是,用已经存在的状态来推出将要发生的状态。

在本题中,状态转移方程为——

  • dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]

3. 初始化dp表

从状态转移方程中,我们发现当 i < 3 时,方程是失效的,因为会出现 dp 表的下标为负数的情况。所以,也就是说,当 i = 0,i = 1,i = 2时,不能依靠状态转移方程推出 dp[i] 的值,则需要我们手动进行初始化,即:

  • dp[0] = 0dp[1] = 1dp[2] = 1

4. 填表顺序

填表顺序即,填 dp 表的顺序。

  • 本题中,我们发现要想推导出当前位置的值,首先要知道前面 3 个 dp 值,所以,填表顺序为从左往右

5. 返回值

  • dp[i] 的含义本来就是第 i 个泰波纳契数的值,所以返回值为dp[n]

代码编写

C++

class Solution {
public:
    int tribonacci(int n) {
        // 特殊情况处理
        if(n == 0) return 0;
        if(n == 1 || n == 2) return 1;
        
        // 创建dp表
        vector<int> dp(n+1);

        // 初始化
        dp[0] = 0; dp[1] = 1; dp[2] = 1;

        // 填表
        for(int i = 3; i <= n; i++)
            dp[i] = dp[i-1] + dp[i-2] + dp[i-3];

        // 返回
        return dp[n];
    }
};

JAVA

class Solution
{
	public int tribonacci(int n)
	{
		// 1. 创建 dp 表
		// 2. 初始化
		// 3. 填表
		// 4. 返回结果
		// 处理边界情况
		if(n == 0) return 0;
		if(n == 1 || n == 2) return 1;
		int[] dp = new int[n + 1];
		dp[0] = 0;
		dp[1] = dp[2] = 1;
		for(int i = 3; i <= n; i++)
			dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3];
		return dp[n];
	}
}

2.三步问题

  • 题目链接:三步问题

  • 题目描述

三步问题。有个小孩正在上楼梯,楼梯有n阶台阶,小孩一次可以上1阶、2阶或3阶。实现一种方法,计算小孩有多少种上楼梯的方式。结果可能很大,你需要对结果模1000000007。

解题流程

1. 状态表示

  • dp[i]:到达第 i 个台阶有多少种方法

2. 状态转移方程

题目中这个小孩的一次最多能跨三个台阶,也就是说当我们要计算到达 i 位置时有多少种方式,可以转化为3种情况:

  • ① 先到达 i-1 位置,然后跨一阶到达 i;
  • ② 先到达 i-2 位置,然后一次性跨两阶到达 i;
  • ③ 先到达 i-3 位置,然后一次性跨三阶到达 i

也就是说,我们可以先到达 i-1 或 i-2 或 i-3 位置,再走一步就可以到达i位置。所以到达 i 位置有多少种方法其实就是——到达前 i 的三个位置的方法数的和。

所以可以得出状态转移方程为:

  • dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]

3. 初始化dp表

从状态转移方程中,我们发现当 i < 3 时,方程是失效的,因为会出现 dp 表的下标为负数的情况。所以,也就是说,当 i = 0,i = 1,i = 2 时,不能依靠状态转移方程推出 dp[i] 的值,则需要我们手动进行初始化,即:

  • dp[1] = 1dp[2] = 2dp[3] = 4(i=0时无意义)

4. 填表顺序

  • 本题中,我们发现要想推导出当前位置的值,首先要知道前面 3 个 dp 值,所以,填表顺序为从左往右

5. 返回值

  • dp[i] 的含义为到达第 i 个台阶有多少种方法,所以返回值为dp[n]

代码编写

C++

class Solution {

const int MOD = 1e9 + 7; // 用来取模

public:
    int waysToStep(int n) {
        if(n == 1) return 1;
        if(n == 2) return 2;
        if(n == 3) return 4;

        // 创建dp表
        vector<int> dp(n+1);

        // 初始化
        dp[1] = 1; dp[2] = 2; dp[3] = 4;

        // 填表
        for(int i = 4; i <= n; i++)
            dp[i] = ((dp[i-1] + dp[i-2]) % MOD + dp[i-3]) % MOD;
        
        // 返回
        return dp[n];
    }
};

JAVA

class Solution
{
	public int waysToStep(int n)
	{
		// 1. 创建 dp 表
		// 2. 初始化
		// 3. 填表
		// 4. 返回值
		int MOD = (int)1e9 + 7;
		// 处理⼀下边界情况
		if(n == 1 || n == 2) return n;
		if(n == 3) return 4;
		int[] dp = new int[n + 1];
		dp[1] = 1; dp[2] = 2; dp[3] = 4;
		for(int i = 4; i <= n; i++)
			dp[i] = ((dp[i - 1] + dp[i - 2]) % MOD + dp[i - 3]) % MOD;
		return dp[n];
	}
}

3.使用最小花费爬楼梯

  • 题目链接:使用最小花费爬楼梯

  • 题目描述

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。
你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。
请你计算并返回达到楼梯顶部的最低花费。

解题流程

1. 状态表示

  • dp[i]:到达第 i 个台阶的最小花费

2. 状态转移方程

由题可知,到达第 i 个台阶,有两种方式:

  • ① 从 i-1 的位置到达 i,花费为 dp[i-1] + cost[i-1]
  • ② 从 i-2 的位置到达 i,花费为 dp[i-2] + cost[i-2]

解释:要想计算 i-1 到达 i 的最小花费,首先要保证到达 i-1 时,所用的花费为最小花费,最小花费为 dp[i-1] 。其次,要想从 i-1 位置到达 i,还得加上 i-1 台阶需要交付的费用,即 cost[i-1]。i-2 同理。

所以,到达i有两种方式,我们选择两种方式中,花费最小的那个,所以需要比较,即 min(方式1,方式2)。所以我们可以得出状态转移方程为:

  • dp[i] = min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2])

3. 初始化dp表

题目上说,你可以从0或1位置开始爬,所以意味着到达0和1位置是免费的,即:

  • dp[0] = 0; dp[1] = 0

4. 填表顺序

  • 本题中,我们发现要想推导出当前位置的值,首先要知道前面2个dp值,所以,填表顺序为从左往右

5. 返回值

  • 有状态表示可知,应返回dp[n]的值;

代码编写

C++

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        int n = cost.size();

        // 创建一个dp表
        vector<int> dp(n+1);

        // 初始化
        dp[0] = 0; dp[1] = 0;

        // 填表
        for(int i = 2; i <= n; i++)
            dp[i] = min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2]);

        // 返回
        return dp[n];
    }
};

JAVA

class Solution
{
	public int minCostClimbingStairs(int[] cost)
	{
		// 1. 创建 dp 表
		// 2. 初始化
		// 3. 填表
		// 4. 返回值
		int n = cost.length;
		int[] dp = new int[n + 1];
		for(int i = 2; i <= n; i++)
			dp[i] = Math.min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
		return dp[n];
	}
}

4.解码方法(中等)

  • 题目链接:解码方法

  • 题目描述

一条包含字母 A-Z 的消息通过以下映射进行了 编码 :
‘A’ -> “1”
‘B’ -> “2”

‘Z’ -> “26”
要 解码 已编码的消息,所有数字必须基于上述映射的方法,反向映射回字母(可能有多种方法)。例如,“11106” 可以映射为:
“AAJF” ,将消息分组为 (1 1 10 6)
“KJF” ,将消息分组为 (11 10 6)
注意,消息不能分组为 (1 11 06) ,因为 “06” 不能映射为 “F” ,这是由于 “6” 和 “06” 在映射中并不等价。
给你一个只含数字的 非空 字符串 s ,请计算并返回 解码 方法的 总数 。

解题流程

1. 状态表示

  • dp[i]:在字符串区间[0,i]上共有dp[i]种解码方法

2. 状态转移方程

对于i位置有多少种解码方法,可从两方面来看:

  • ① i 位置可以单独解码成功;
  • ② i 位置与 i-1 位置组合成一个字母解码成功;

对上面两种情况单独进行分析,每种情况又可以分为两种情况:

对于单独解码的情况

  • Ⅰ. 解码成功:如果 s[i] 不等于零,即可解码成功 (因为 0 没有对应的编码)。此时 dp[i] 就等于 dp[i-1] ,因为 i 可以单独解码,不会影响总的方法,例如 [9] [9,6] ,加入了 6 之后并不会影响编码结果;
  • Ⅱ. 解码失败:如果 s[i] 等于 0 ,解码失败,dp[i] = 0;因为一个字符串中,当你以某种方法进行解码,留下了最后一个数字是个 0,解码失败了,证明你的方法是错的。例如下面这个字符串就会解码失败。
    〖动态规划60题〗泰波纳契数列模型,动态规划60题,动态规划,算法

对于组合解码的情况

  • Ⅰ. 解码成功:如果 i 位置与 i-1 位置组合起来是 [10,26] 的数字,即解码成功,此时 dp[i] = dp[i-2],原因同上;
  • Ⅱ. 解码失败:如果两位组合是 ”06“、”09“ 这样的数字,即为解码失败,若大于 26,也为失败,此时 dp[i]=0。

综上所述,我们可以得到状态转移方程为:

  • 当 s[i] 上的数在[1, 9] 区间上时: dp[i] += dp[i - 1]
  • 当 s[i - 1] 与 s[i] 上的数结合后,在 [10, 26] 之间的时候: dp[i] +=dp[i - 2] ;

3. 初始化dp表

方法一 直接初始化

与之前的情况相同,由于需要用到 dp[i-1],dp[i-2] 的值,所以i需大于等于 2,dp[0],dp[1] 的值需要手动赋值。

  • 如果 s[0] 等单独解码成功,则 dp[0] = 1,否则dp[0] = 0
  • 如果 s[1] 能单独解码成功,则 dp[1] += dp[0];
  • 如果 s[1] 与 s[0] 组合解码成功,则 dp[1] += 1;

方法二 辅助位初始化
可以在最前⾯加上⼀个辅助结点,帮助我们初始化。使⽤这种技巧要注意两个点:

  • 辅助结点里面的值要保证后续填表是正确的
  • 下标的映射关系

4. 填表顺序

  • 本题中,我们发现要想推导出当前位置的值,首先要知道前面2个 dp 值,所以,填表顺序为从左往右

5. 返回值

  • 方法一:应该返回dp[n - 1]的值,表⽰在 [0, n - 1] 区间上的编码方法。
  • 方法二:由于多增加了一个辅助位,所以返回值要后移一位

代码编写

C++ 方法一

class Solution {
public:
    int numDecodings(string s) {
        int n = s.size();

        // 创建一个dp表
        vector<int> dp(n);

        // 初始化
        dp[0] = s[0] != '0';

        // 处理边界情况
        if (n == 1) return dp[0];

        if (s[1] <= '9' && s[1] >= '1') dp[1] += dp[0];

        int t = (s[0] - '0') * 10 + (s[1] - '0');
        if (t <= 26 && t >= 10) dp[1] += 1;

        // 填表
        for (int i = 2; i < n; i++)
        {
            if (s[i] == '0') dp[i] = 0;

            if (s[i] <= '9' && s[i] >= '1') dp[i] += dp[i - 1];

            int t = (s[i - 1] - '0') * 10 + (s[i] - '0');
            if (t <= 26 && t >= 10) dp[i] += dp[i - 2];
        }

        // 返回
        return dp[n - 1];
    }
};

C++ 方法二

class Solution {
public:
    int numDecodings(string s) {
        int n = s.size();

        // 创建一个dp表
        vector<int> dp(n+1);

        // 初始化
        dp[0] = 1; // 辅助位
        dp[1] = s[0] != '0';

        // 填表
        for(int i = 2; i <= n; i++)
        {
            if(s[i] <= '9' && s[i] >= '1') dp[i] += dp[i-1];

            int t = (s[i-1] - '0')* 10 + (s[i] - '0');
            if(t <= 26 && t >= 10) dp[i] += dp[i-2];
        }

        // 返回
        return dp[n-1];
    }
};

JAVA文章来源地址https://www.toymoban.com/news/detail-525143.html

class Solution
{
	public int numDecodings(String ss)
	{
		// 1. 创建 dp 表
		// 2. 初始化
		// 3. 填表
		// 4. 返回值
		int n = ss.length();
		char[] s = ss.toCharArray();
		int[] dp = new int[n + 1];
		dp[0] = 1; // 保证后续填表是正确的
		if(s[1 - 1] != '0') dp[1] = 1;
		for(int i = 2; i <= n; i++)
		{
			// 先处理第⼀种情况
			if(s[i - 1] != '0') dp[i] += dp[i - 1];
			// 处理第⼆种情况
			int tt = (s[i - 2] - '0') * 10 + s[i - 1] - '0';
			if(tt >= 10 && tt <= 26) dp[i] += dp[i - 2];
		}
		return dp[n];
	}
}

到了这里,关于〖动态规划60题〗泰波纳契数列模型的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 动态规划之斐波拉契数列模型

    动态规划的介绍: 动态规划是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划常常适用于有重叠子问题和最优子结构性质的问题。 动态规划最核心的思想,就在于 拆分子问题

    2024年02月13日
    浏览(38)
  • 60题学会动态规划系列:动态规划算法第三讲

    简单多状态问题 文章目录 一.按摩师 二.打家劫舍系列 三.删除并获得点数 四.粉刷房子 力扣链接:力扣 一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,

    2024年02月08日
    浏览(49)
  • 动态规划专训1——泰波那契数列模型

    动态规划的思想:将一个问题分隔为若干个子问题,完成子问题得到结构再得到最终的答案 动态规划往往解题步骤固定,分为以下几步 1.找出状态表示 2.完成状态转移方程 3.初始化 4.填表顺序 5.返回值 后面三步偏重细节,二解题的核心就在于前两步,所以要想练好动态规划

    2024年04月29日
    浏览(36)
  • 60题学会动态规划系列:动态规划算法第一讲

    坚持就是胜利 - -  文章目录 1.第N个泰波那切数 2.三步问题 3.使用最小花费爬楼梯 4.解码方法 力扣链接:力扣 泰波那契序列 Tn 定义如下:  T0 = 0, T1 = 1, T2 = 1, 且在 n = 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2 给你整数  n ,请返回第 n 个泰波那契数 Tn 的值。  首先我们分析一下

    2024年02月06日
    浏览(45)
  • 60题学会动态规划系列:动态规划算法第二讲

    都是路径问题~ 文章目录 1.不同路径 2.不同路径II 3.礼物的最大价值 4.下降路径最小和 5.最小路径和 力扣链接:力扣 一个机器人位于一个  m x n   网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在

    2024年02月07日
    浏览(59)
  • 60题学会动态规划系列:动态规划算法第四讲

    买卖股票相关的动态规划题目 文章目录 1. 买卖股票的最佳时机含冷冻期 2. 买卖股票的最佳时期含⼿续费 3. 买卖股票的最佳时机III 4. 买卖股票的最佳时机IV 力扣链接:力扣 给定一个整数数组 prices ,其中第    prices[i]  表示第  i  天的股票价格 。​ 设计一个算法计算出最

    2024年02月13日
    浏览(36)
  • 60题学会动态规划系列:动态规划算法第五讲

    子数组系列题目 文章目录 1.最大子数组和 2.环形子数组的最大和 3.乘积最大数组 4.乘积为正数的最长子数组长度 5.等差数列划分 6.最长湍流子数组 7.单词拆分 8.环绕字符串中唯一的子字符串 力扣链接:力扣 给你一个整数数组  nums  ,请你找出一个具有最大和的连续子数组(

    2024年02月15日
    浏览(69)
  • 动态规划入门:斐波那契数列模型以及多状态(C++)

        动态规划(Dynamic programming,简称 DP)是一种解决多阶段决策问题的算法思想。它将问题分解为多个阶段,并通过保存中间结果来避免重复计算,从而提高效率。 动态规划的解题步骤一般分为以下几步: 思考状态表示,创建dp表(重点) 分析出状态转移方程(重点) 初始化 确定

    2024年02月11日
    浏览(44)
  • Java【动态规划】斐波那契数列模型, 图文思路详解 + 代码实现

    本篇总结动态规划中的 斐波那契数列模型 的解法和思路 按照以下流程进行分析题目和代码编写 思路分析步骤 代码编写步骤 1, 状态表示 1, 构造 dp 表 2, 状态转移方程 2, 初始化+边界处理 3, 初始化 3, 填表(抄状态转移方程) 4, 填表顺序 4, 返回结果 5, 返回值 / OJ链接 题目分析

    2024年02月08日
    浏览(59)
  • 【动态规划专栏】专题一:斐波那契数列模型--------2.三步问题

    本专栏内容为:算法学习专栏,分为优选算法专栏,贪心算法专栏,动态规划专栏以及递归,搜索与回溯算法专栏四部分。 通过本专栏的深入学习,你可以了解并掌握算法。 💓博主csdn个人主页:小小unicorn ⏩专栏分类:动态规划专栏 🚚代码仓库:小小unicorn的代码仓库🚚

    2024年02月21日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包