赞
踩
目录
本文通过对斐波那契数列问题引入动态规划,这也是动态规划问题中比较简单的一类问题,通过这个模型我们一起来学习什么是动态规划算法并且动态规划问题的基本做题步骤是什么
斐波那契数列是这样一个数列:从第三项开始,每一项都等于前两项之和。数列的前几项为:0, 1, 1, 2, 3, 5, 8, 13, 21, ...。这个数列的特性使得它在很多实际问题中都有着重要的应用。
在计算斐波那契数列时,我们可以使用递归的方式。然而,递归方式在计算较大的斐波那契数时,会存在大量的重复计算,效率非常低。而动态规划技术可以有效地避免这种重复计算,显著提高计算效率。
动态规划的基本思想是将问题分解为若干个子问题,并将这些子问题的解保存起来,以便在计算新的子问题时能够直接利用之前的结果,避免重复计算。在斐波那契数列问题中,我们可以使用动态规划技术,从下往上计算斐波那契数列,并将每个计算结果保存起来,这样在计算较大的斐波那契数时,就可以直接使用之前保存的结果,通过空间换时间的操作,大大提高了计算效率。
下面是一个C++代码示例,展示了如何使用动态规划来计算斐波那契数列:
- #include<iostream>
-
- const int N = 100;
- int dp[N];
-
- int main()
- {
- dp[0] = 1,dp[1] = 1;
- int n;cin>>n;
- for(int i=2;i<=n;i++)
- dp[i] = dp[i-1] + dp[i-1];
- cout<<dp[n]<<endl;
- return 0;
- }
在上述代码中,我们创建了一个列表dp
来保存斐波那契数列的值。初始时,列表的前两个元素被设置为斐波那契数列的前两项。然后,我们通过一个循环,从第三项开始,依次计算斐波那契数列的每一项,并将结果保存在dp
列表中。最后,返回dp[n]
,即第n项斐波那契数的值。
动态规划在斐波那契数列中的核心思想是通过保存子问题的解来避免重复计算。具体方法是将问题分解为子问题,并按照某种顺序(如上述示例中的从下往上)依次解决这些子问题,同时保存每个子问题的解。在计算新的子问题时,如果其解已经保存,则直接利用保存的解,否则通过解决更小的子问题来得到其解,并将解保存起来。
在分析题目时分为五步:
dp[i]代表什么? 一般可以 以i为结尾 或 以i为起点 做状态表示
在编写代码时分为四步:
本题很简单明了,题目里面就已经给出了状态转移方程,由此,我们定义dp[n]是第n个泰波斐契数
代码:
- class Solution {
- public:
- int m = 40;
- int tribonacci(int n)
- {
- //空间优化
- if(n == 0) return 0;
- if(n == 1 || n == 2) return 1;
-
- int a = 0,b = 1, c = 1,d;
- for(int i=3;i<=n;i++)
- {
- d = a + b + c;
- //滚动操作
- a = b; b = c; c = d;
- }
- return d;
- }
- };
由于每个数只会用到它的前三个数,所以我们可以使用滚动数组来进行空间优化
分析:
代码:
- class Solution {
- public:
- int waysToStep(int n)
- {
- int MOD = 1e9 + 7;
- //创建dp表
- vector<int> dp(10+n);
- //初始化
- 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];
- }
- };
题意是结果取模即可,但是因为数值过大,计算过程中就会爆数据范围,所以只能每做一次加法取一次模,和对结果取模等价; 注意,取模运算具有分配律:(A+B)%C = A%C + B%C
分析:题中表示,从第i层向上爬,需要花费cost[i]的体力值,可以从0、1层向上爬,也就死选择到0/1层并不花费体力值;
代码:
- class Solution {
- public:
- int minCostClimbingStairs(vector<int>& cost)
- {
- int n = cost.size();
- vector<int> dp(10+n);
- dp[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];
- }
- };
看到这里,可能有读者会有疑惑,分析状态表示时这三个题怎么都是以i为结尾分析,做题步骤中不是还有以i为起点分析吗?其实都可以,做题的时候觉得怎样更方便就选择怎样,那么这道题我们也以i为起点来举例分析一下
代码:
- class Solution {
- public:
- int minCostClimbingStairs(vector<int>& cost)
- {
- //dp[i]:以i位置出发到达楼顶的最小花费
- //dp[i] = min(dp[i+1]+cost[i],dp[i+2]+cost[i]);
- int n = cost.size();
- vector<int> dp(n+10);
- dp[n] = 0;dp[n-1] = cost[n-1];
- for(int i=n-2;i>=0;i--)
- dp[i] = cost[i] + min(dp[i+1],dp[i+2]);
-
- return min(dp[0],dp[1]);
- }
- };
这道题怎么分析都可以,建议两种视角都看一遍加深理解
分析:一个字符要么单独解码,要么合并另一个字符解码,合并时前导0情况表示解码失败
代码:
- class Solution {
- public:
- int numDecodings(string s)
- {
- int n = s.size();
- vector<int> dp(10+n);
- if(s[0]-'0' != 0) dp[0] = 1;
- else dp[0] = 0;
- if(s[0]-'0' && s[1]-'0') dp[1]++;
- int t = (s[0]-'0')*10 + s[1] - '0';
- if(t>=10 && t<= 26) dp[1]++;
-
- for(int i=2;i<n;i++)
- {
- if(s[i] != '0') dp[i] += dp[i-1];
- int t = (s[i-1] - '0')*10 + s[i] - '0';
- if(10<=t && t<=26) dp[i] += dp[i-2];
- }
-
- return dp[n-1];
- }
- };
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。