🤚我的博客
- 欢迎光临我的博客:
https://blog.csdn.net/qq_52434217?type=blog
🥛前言
动态规划是常见的算法思路,动态规划在计算过程中保存了部分计算结果到内存中,以便于在进行下一次计算时可以直接从内存中获取到而不用再进行计算,可以降低时间复杂度。
动态规划也一直都是一个比较难以形成思路的算法思想。这篇文章虽然是一个简单题,但是记录了动态规划算法的解题思路,方便形成一个固定的解题思路。
📖正文
💠爬楼梯
题目描述
假设你正在爬楼梯。需要 n
阶你才能到达楼顶。每次你可以爬 1
或 2
个台阶。你有多少种不同的方法可以爬到楼顶呢?
思考过程
1、 确定第n个状态
面对动态规划时,首先需要确定第n
个状态。这里的第n
个状态就是爬上第n
阶的方法,记作f(n)
。
根据题目,每次可以爬1
或2
个台阶,所以到达n
阶的情况有从n-1
阶爬1
个台阶与从n-2
阶爬2
个台阶。
假设到达第n
阶有f(n)
种方法,到达n-1
阶有f(n-1)
种方法,到达n-2
阶有f(n-2)
种方法。
所以到达第n
阶有f(n)
种方法,且
f
(
n
)
=
f
(
n
−
1
)
+
f
(
n
−
2
)
f(n)=f(n-1)+f(n-2)
f(n)=f(n−1)+f(n−2)
2、考虑边界条件
考虑特殊情况,f(0)=1;f(1)=1
。可以得到最后的结果
细节:需要从初始值遍历到n
且包含n
,否则只是计算了n-1
的方法。至于i++
与++i
无区别。
解题代码
class Solution{
public int climbStairs(int n){
if(n==0 || n==1)return 1;
int arr[n+1];
arr[0] = 1;
arr[1] = 1;
for(int i=2;i<=n;++i){
arr[i]=arr[i-1]+arr[i-2];
}
return arr[n];
}
}
空间复杂度优化
我们发现空间复杂度为O(n)
,尝试分析一下有没有优化空间。
写下递推关系
f
(
n
)
=
f
(
n
−
1
)
+
f
(
n
−
2
)
f(n)=f(n-1)+f(n-2)
f(n)=f(n−1)+f(n−2)
f
(
n
−
1
)
=
f
(
n
−
2
)
+
f
(
n
−
3
)
f(n-1)=f(n-2)+f(n-3)
f(n−1)=f(n−2)+f(n−3)
可以发现这里当f(n)
是当前状态,f(n-1)
是上一状态,f(n-2)
是上上一状态;当f(n-1)
是当前状态,f(n-2)
是上一状态,f(n-3)
是上上状态。不难发现,我们只要将上一状态变为上上一状态,当前状态变为上一状态,那么我们只需要用三个变量接收就可以。分别用res
,b
和a
表示当前状态,上一状态和上上一个状态。然后将动态规划转换成循环。这里考虑边界条件,当n=1
时,res=1
;当n=0
时,res=1
;
假设从n=0
开始,此时需要初始化b=0
,a=0
,res=1
。这里的物理意义是假设n=0
还有上一个状态,那么上一个状态就是res
。如此下去n=1
会有上上一个状态。故不能从n=0
开始。
如果从n=1
开始,则假设n=1
为当前状态时,n=0
为上一状态,所以初始化时res=1
,同时a=0
,b=0
。此时循环就只能从n=1
开始,n=0
直接返回不参与运算。
class Solution{
public int climbStairs(int n){
int a=0,b=0,res=1;
for(int i=1;i<=n;++i){
a = b; // 前前值
b = res; // 前值
res = a + b;
}
return res;
}
}
💠使用最小花费爬楼梯
题目描述
给你一个整数数组 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
思考过程
1、确定第n
个状态
假设cost
长度为10
,则该题求解的是到第10
阶的最小花费。首先确定第n
个状态。第n
个状态就是到第n
阶需要的花费。根据题目描述得知,以第二个例子为例,爬到顶部的最小花费是从第9阶爬到第10阶的花费与从第8阶爬到第10阶的最小花费。那么上第10阶的最小花费可以表示为
f ( 10 ) = m i n ( ( f ( 9 ) + c o s t [ 9 ] ) , ( f ( 8 ) + c o s t [ 8 ] ) ) f(10) = min((f(9) + cost[9]),(f(8)+cost[8])) f(10)=min((f(9)+cost[9]),(f(8)+cost[8]))
那么第n
个状态可以表示为到第n
阶所需要的最小花费。上第n
阶有两种方法,分别是从n-1
阶爬上来和从n-2
阶爬上来,那么花费就是f(n-1)
和f(n-2)
。所以第n
个状态就是
f
(
n
)
=
m
i
n
f
(
n
−
1
)
+
c
o
s
t
[
n
−
1
]
,
f
(
n
−
2
)
+
c
o
s
t
[
n
−
2
]
f(n)=min{f(n-1)+cost[n-1],f(n-2)+cost[n-2]}
f(n)=minf(n−1)+cost[n−1],f(n−2)+cost[n−2]
。这里的min{a,b}
表示取a
, b
中的最小值。
2、确定边界条件
题目已经告诉我们可以从第0
阶开始,也可以从第1
阶开始,所以有f(0)=0
、f(1)=0
。即有n<2
时返回0
。
解题代码
class Solution {
public int minCostClimbingStairs(int[] cost) {
int len = cost.length;
int[] total = new int[len];
Arrays.fill(total, -1);
return Math.min(totalCostOfNumber(total,cost,len-1)+cost[len-1],
totalCostOfNumber(total,cost,len-2)+cost[len-2]);
}
//到达第stairNumber阶需要的cost
public int totalCostOfNumber(int[] total,int[] cost,int stairNumber){
int totalCost = 0;
if (stairNumber < 2) return 0;
if(total[stairNumber] != -1) return total[stairNumber];
// 到第stairNumber-1阶总的cost与stairNumber-1到第stairNumber的cost之和
totalCost = Math.min(totalCostOfNumber(total,cost,stairNumber-1)+cost[stairNumber-1],
totalCostOfNumber(total,cost,stairNumber-2)+cost[stairNumber-2]);
total[stairNumber] = totalCost;
return totalCost;
}
}
空复杂度优化
上面的解法的空间复杂度依旧为O(n)
,所以我们尝试写下递推式进行分析
f
(
10
)
=
m
i
n
(
(
f
(
9
)
+
c
o
s
t
[
9
]
)
,
(
f
(
8
)
+
c
o
s
t
[
8
]
)
)
f(10) = min((f(9) + cost[9]),(f(8)+cost[8]))
f(10)=min((f(9)+cost[9]),(f(8)+cost[8]))
f
(
9
)
=
m
i
n
(
(
f
(
8
)
+
c
o
s
t
[
8
]
)
,
(
f
(
7
)
+
c
o
s
t
[
7
]
)
)
f(9) = min((f(8) + cost[8]),(f(7)+cost[7]))
f(9)=min((f(8)+cost[8]),(f(7)+cost[7]))
可以发现这个题与上一题一样可以采用三个变量接收f(n)
、f(n-1)
和f(n-2)
。这样就可以减小空间复杂度。
class Solution {
public int minCostClimbingStairs(int[] cost) {
int a = 0, b = 0;
for (int i = 1; i < cost.length; i++) {
int res = Math.min(a + cost[i], b + cost[i-1]);
a = b;
b = res;
}
return b;
}
}
参考资料
作者:灵茶山艾府 链接:https://leetcode.cn/problems/min-cost-climbing-stairs/solutions/2569116/jiao-ni-yi-bu-bu-si-kao-dong-tai-gui-hua-j99e/ 来源:力扣(LeetCode)
💠END
动态规划的难点在于如何确定第n
个状态f(n)
,所以需要多加练习。
数据结构与算法题还在继续更新,虽然是学习的大佬的思路。但是自己能进行输出也是一种学习的方法和技巧。
一起加油!🆙🆙🆙
🏕️公众号
欢迎关注小夜的公众号,一个立志什么都能会的研究生。有不懂的地方请留言踢我!