题目描述
小U最近决定挑战一座非常高的楼梯,每次他可以选择走一步或两步,但有一个重要的限制:他不能连续走两步。因此,小U想知道他总共有多少种不同的方式可以从楼梯的底部走到顶端。你需要帮他计算在给定的楼梯层数下,小U有多少种走法。
测试样例
样例1:
输入:
n = 2
输出:2
样例2:
输入:
n = 3
输出:3
样例3:
输入:
n = 4
输出:4
题目分析
这题题干有一点模糊,不能连续走两步 实际指的是不能连续走两次两步。
其实这道题大家应该很眼熟,如果没有不能连续走两步这一限制的话这就是一个典型的爬楼梯问题,也就是斐波那契动态规划问题(好像这种题变种还挺多的,之前还碰到过一个限制某些 “楼梯” 不能走的题)。所以这题我们第一反应应该是用动态规划解决。
思路一:动态规划
知道要用动态规划,但是重点是怎么假设出合适的状态以及找出状态转移方程?
在没有不能连续走两步这一限制的情况下,状态转移方程就是 ,我们考虑从这个方程入手进行改造。假设 对应的是走到第 i 层楼梯可行的方案数,那么 可以从哪些状态转化而来呢?很明显它跟 和 有关,但是此时 的值不能直接写成 ,因为从 转移到 是有前提条件的,那就是走到第 i-2 层楼梯的最后一步的跨度不能是2(不然就跟不能连续走两次两步这个限制冲突了),所以在状态转移时我们还需要关注每一步的步长,因此必须使用二维dp。
假设 表示到达第 i 层楼梯走的最后一步的步长是1, 代表到达第 i 层楼梯的最后一步步长是 2,那么结合我们上面的分析可知,状态转移方程如下:
第一个转移方程:dp[i][0] = dp[i-1][0] + dp[i-1][1], 这里 dp[i][0] 限制了前一步步长为1,那么对倒数第二步的步长就没有要求了,所以当最后一步步长为 1 时到达第 i 层的方案数(dp[i][0])就等于到达第 i-1 层的方案数(dp[i-1][0] + dp[i-1][1])
第二个转移方程:dp[i][1] = dp[i-2][0],这里 dp[i][1] 限制了前一步步长为 2,那显然我们倒数第二步的步长只能是 1,也就是说 dp[i][1] 只能从 dp[i-2][0] 转移过来。
根据状态转移方程更新完 数组后,我们要求的最终方案数即为 。
这里在进行状态更新之前需要注意一下 数组状态的初始化,主要是两个值: 和 , 表示跨一步到第一层楼梯的方案数,显然为 1,而 也需要赋值为 1,否则当 n = 2 时我们的答案就错了(这里我暂时还没想通该怎么解释这个 dp[0][0],如果有大佬知道的话欢迎在评论区指教)。
完整代码如下:
def solution(n):
if n == 0 or n == 1:
return 1
dp = [[0,0] for _ in range(n+1)]
dp[0][0], dp[1][0] = 1, 1
for i in range(2, n+1):
# dp[i][0]表示到达第i层的最后一步走了一步
dp[i][0] = dp[i-1][0] + dp[i-1][1]
# dp[i][1]表示到达第i层的最后一步走了两步,那前一步只能取走一步的情况
dp[i][1] = dp[i-2][0]
return dp[n][0] + dp[n][1]
思路二:暴力搜索
虽然 DP 用起来很爽,但是如果思路一我没想到或者说想不出来状态转移方程怎么办呢?对于这道题,不妨试一下 DFS。
我们从第一层开始往后遍历,枚举从第 1 层走到第 n 层的所有可能情况并计数即可。
完整代码如下:
def dfs(pre, idx, n, l):
# idx表示走到了idx位置
if idx >= n:
l[0] += 1
return
if pre == 2:
dfs(1, idx+1, n, l)
else:
dfs(1, idx+1, n, l)
dfs(2, idx+2, n, l)
def solution(n):
if n == 1 or n == 0: return 1
l = [0]
dfs(1, 1, n, l)
# print(l[0])
return l[0]
这里我写的 dfs 函数包含四个变量,idx 表示当前楼梯的位置,n 是函数终止的边界,l 这里我偷了个懒,直接传了一个单值列表用来计数,而 pre 则是用来记录到达当前第 idx 层楼梯的前一步跨度。dfs 终止条件很简单,当 idx == n 时就结束了(注意:不是 idx > n ,因为 idx 代表的是到达第 idx 层楼梯,所以当 idx == n 时我们就已经走到终点了)。dfs 函数主体比较简单,根据上一步跨度 pre 的情况,我们按照规则对后面的楼梯继续进行搜素即可。具体来说就是如果 pre == 2,也就是我们上一步已经走了 2 步了,那么下一步只能走 1 步,也就是 dfs(1, idx+1, n, l);而如果 pre == 1,也就是我们上一步走了 1 步,那下一步就可以随便走了,也就是 dfs(1, idx+1, n, l) 与 dfs(2, idx+2, n, l) 两种状态转移。