赞
踩
动态规划的基本思想是利用之前已经计算过的结果,通过递推关系式来计算当前问题的解。它具有以下关键特点:
动态规划可以分为两种主要类型:自顶向下的记忆化搜索和自底向上的递推法。
动态规划常用于求解具有最优子结构和重叠子问题性质的问题,例如最短路径问题、背包问题、编辑距离问题等。
典型的动态规划问题包括:
动态规划算法是一种强大的算法设计思想,可以在很多问题中得到应用,但需要注意问题是否满足最优子结构和重叠子问题的条件。
题目链接:https://leetcode.cn/problems/n-th-tribonacci-number/
泰波那契序列 Tn 定义如下:
T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2
给你整数 n
,请返回第 n 个泰波那契数 Tn 的值。
示例 1:
输入:n = 4
输出:4
解释:
T_3 = 0 + 1 + 1 = 2
T_4 = 1 + 1 + 2 = 4
示例 2:
输入:n = 25
输出:1389537
提示:
0 <= n <= 37
answer <= 2^31 - 1
。思路
动态规划的题目主要分为5个步骤。
1.状态表示:这道题可以根据题目要求直接给出,dp[i]等于第i个泰波纳契数。
2.状态转移方程:同样原题已经给出,dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3] 。
3.初始化:我们只需要将前三个数初始化即可,防止越界。
4.填表顺序:从左往右。
5.返回值:dp[n]。
代码
class Solution {
public:
int tribonacci(int n) {
int dp[38];
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];
}
};
题目链接:https://leetcode.cn/problems/three-steps-problem-lcci/
三步问题。有个小孩正在上楼梯,楼梯有n阶台阶,小孩一次可以上1阶、2阶或3阶。实现一种方法,计算小孩有多少种上楼梯的方式。结果可能很大,你需要对结果模1000000007。
示例1:
输入:n = 3
输出:4
说明: 有四种走法
示例2:
输入:n = 5
输出:13
提示:
思路
dp[i]
表示到达第 i
个位置时的总方法数。dp[i]
可以由前三个状态转移而来:dp[i - 1]
、dp[i - 2]
、dp[i - 3]
。因此,状态转移方程为 dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]
。dp[1] = 1
、dp[2] = 2
、dp[3] = 4
。dp[i]
的值。dp[n]
的值,即到达第 n
个位置时的总方法数。这样的动态规划问题通常可以通过填表的方式解决,遵循以上步骤可以得到最终的解。
代码
class Solution {
const int Mod = 1e9+7;
public:
int waysToStep(int n) {
if(n==1||n==2) return n;
if(n==3) return 4;
vector<long long> 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];
}
};
题目链接:https://leetcode.cn/problems/min-cost-climbing-stairs/
给你一个整数数组 cost
,其中 cost[i]
是从楼梯第 i
个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。
你可以选择从下标为 0
或下标为 1
的台阶开始爬楼梯。
请你计算并返回达到楼梯顶部的最低花费。
示例 1:
输入:cost = [10,15,20]
输出:15
解释:你将从下标为 1 的台阶开始。
- 支付 15 ,向上爬两个台阶,到达楼梯顶部。
总花费为 15 。
示例 2:
输入:cost = [1,100,1,1,1,100,1,1,100,1]
输出:6
解释:你将从下标为 0 的台阶开始。
- 支付 1 ,向上爬两个台阶,到达下标为 2 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 4 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 6 的台阶。
- 支付 1 ,向上爬一个台阶,到达下标为 7 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 9 的台阶。
- 支付 1 ,向上爬一个台阶,到达楼梯顶部。
总花费为 6 。
提示:
2 <= cost.length <= 1000
0 <= cost[i] <= 999
思路一
dp[i]
表示到达第 i
个位置时的最小花费。注意:到达第 i
个位置的时候,第 i
位置的花费不需要算上。dp[i]
可以由前两个状态转移而来:dp[i - 1]
和 dp[i - 2]
。因此,状态转移方程为 dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2])
。dp[0] = dp[1] = 0
,因为不需要任何花费就可以直接站在第 0 层和第 1 层上。dp[i]
的值。dp[n]
的值,即到达第 n
个位置时的最小花费。代码一
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
int n=cost.size();
vector<int> dp(n+1,0);
dp[0]=dp[1]=0;
for(int i=2;i<=n;++i)
dp[i]=min(cost[i-1]+dp[i-1],cost[i-2]+dp[i-2]);
return dp[n];
}
};
思路二
dp[i]
表示从第 i
个位置出发到达楼顶时的最小花费。dp[i]
可以由后两个状态转移而来:dp[i + 1]
和 dp[i + 2]
。因此,状态转移方程为 dp[i] = min(dp[i + 1], dp[i + 2]) + cost[i]
。dp[n - 1] = cost[n - 1]
和 dp[n - 2] = cost[n - 2]
。dp[i]
的值。dp[0]
和dp[1]
中最小的值。代码二
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
int n=cost.size();
vector<int> dp(n,0);
dp[n-1]=cost[n-1],dp[n-2]=cost[n-2];
for(int i=n-3;i>=0;--i)
dp[i]=min(cost[i]+dp[i+1],cost[i]+dp[i+2]);
return min(dp[0],dp[1]);
}
};
题目链接:https://leetcode.cn/problems/decode-ways/
一条包含字母 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
,请计算并返回 解码 方法的 总数 。
题目数据保证答案肯定是一个 32 位 的整数。
示例 1:
输入:s = "12"
输出:2
解释:它可以解码为 "AB"(1 2)或者 "L"(12)。
示例 2:
输入:s = "226"
输出:3
解释:它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。
示例 3:
输入:s = "06"
输出:0
解释:"06" 无法映射到 "F" ,因为存在前导零("6" 和 "06" 并不等价)。
提示:
1 <= s.length <= 100
s
只包含数字,并且可能包含前导零。思路
状态表示: 定义状态 dp[i]
表示字符串中 [0, i]
区间上,一共有多少种编码方法。
状态转移方程: 分析 i
位置的 dp
值,有两种解码情况:
s[i]
在区间 [1, 9]
内时,说明 s[i]
可以单独解码,此时 dp[i] += dp[i - 1]
。s[i - 1]
与 s[i]
结合后在区间 [10, 26]
内时,说明前两个字符有一种编码方式,此时 dp[i] += dp[i - 2]
。综上所述,状态转移方程为:
dp[i] = (s[i] != '0' ? dp[i - 1] : 0) + (10 <= stoi(s.substr(i - 1, 2)) <= 26 ? dp[i - 2] : 0)
初始化: 需要初始化前两个位置的值:
dp[0]
:
s[0] == '0'
时,没有编码方法,结果 dp[0] = 0
。s[0] != '0'
时,能编码成功,此时 dp[0] = 1
。dp[1]
:
s[1]
在区间 [1, 9]
内时,能单独编码,此时 dp[1] += dp[0]
。s[0]
与 s[1]
结合后的数在区间 [10, 26]
内时,说明前两个字符有一种编码方式,此时 dp[1] += 1
。填表顺序: 从左往右填表,逐步计算出 dp[i]
的值。
返回值: 返回 dp[n - 1]
的值,表示在 [0, n - 1]
区间上的编码方法数量。
代码
class Solution { public: int numDecodings(string s) { int n=s.size(); vector<int> dp(n); dp[0]=s[0]!='0'; if(n==1) return dp[0]; if(s[1]>='1'&&s[1]<='9') dp[1]+=dp[0]; int t=(s[0]-'0')*10+s[1]-'0'; if(t>=10&&t<=26) dp[1]+=1; for(int i=2;i<n;++i){ if(s[i]>='1'&&s[i]<='9') dp[i]+=dp[i-1]; int t=(s[i-1]-'0')*10+s[i]-'0'; if(t>=10&&t<=26) dp[i]+=dp[i-2]; } return dp[n-1]; } };
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。