Why not absolutely reduce the complexity constant almost by half(i.e.=>O(n/2)) with few efforts?
——简单一波思考减少一半常数!
Description
You are climbing a staircase. It takes n
steps to reach the top.
Each time you can either climb 1
or 2
steps. In how many distinct ways can you climb to the top?
Example 1:
Input: n = 2 Output: 2 Explanation: There are two ways to climb to the top. 1. 1 step + 1 step 2. 2 steps
Example 2:
Input: n = 3 Output: 3 Explanation: There are three ways to climb to the top. 1. 1 step + 1 step + 1 step 2. 1 step + 2 steps 3. 2 steps + 1 step
Intuition
It doesn't take an Enstein to identify how to solve this through a linear dp for those a bit familiar with dp.
Nevertheless, the "symmetry" in the problem inspires us to employ a "Meet in the Middle"(a tactic in Search)-like thought to optimize the solution.
Approach
First, suppose now we figure out f[i] = f[i-2] + f[i-1]
with our aim being f[n]
.
In the problem, based on the thought of "Meet in the Middle", the "jumper's route" can be divided into two parts: the first "half" and the second one. Furthermore, when we've calculated "half the f[n]
s", say f[1...m]
where , which are also "correct results" as to "the second half", we want to directly calculate the final answer through multiplication(s).
Surely we can count in f[m] * f[n-m]
, but note that the jumper may not stop on the mth step sometime in a legal "jumping way". However, in such a case the jumper must stop at the (m-1)th step sometime, followed by a "2 steps". Such a situation has a contribution of f[m-1] * f[n-m-1]
.
This way, obviously we reduce the complexity constant almost by half.
Complexity
- Time complexity:O(n)
- Space complexity:O(n)/O(1)(see Discussion)
Code
class Solution:
def climbStairs(self, n: int) -> int:
if n == 1:
return 1
m = (n+1) // 2
f = [0]*(m+1)
f[0], f[1] = 1, 1
for i in range(2,m+1):
f[i] = f[i-2] + f[i-1]
''' Key observation:
1. Stop at the mth step sometime
2. Stop at the (m-1)th step sometime, followed by a "2 steps"
'''
return f[m]*f[n-m] + f[m-1]*f[n-m-1]
Discussion
- Of course you can use other terms, such as "Divide and Conquer" to summarize the core thought here.
- This method seems to apply to other problems.
- I guess that one (who may not know dp) implementing a DFS solution based on the above-mentioned thought can also safely pass this problem.
- Just maintaing "
a, b, c
"(known by many) to reduce memory complexity to O(1) still applies here.