小白的首篇LeetCode(国际版)英文题解 - Problem. 70

Why not absolutely reduce the complexity constant almost by half(i.e.=>O(n/2)) with few efforts?

——简单一波思考减少一半常数!

Climbing Stairs - LeetCodeicon-default.png?t=N7T8https://leetcode.com/problems/climbing-stairs/solutions/4586060/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 m=\lceil\frac n2\rceil, 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

  1. Of course you can use other terms, such as "Divide and Conquer" to summarize the core thought here.
  2. This method seems to apply to other problems.
  3. 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.
  4. Just maintaing "a, b, c"(known by many) to reduce memory complexity to O(1) still applies here.

你可能感兴趣的:(Leetcode,刷题,leetcode,算法,职场和发展)