跳至主要內容

70, 爬楼梯

Mike大约 3 分钟dynamic programmingeasymathdynamic programmingmemoization

一、题目描述

假设你正在爬楼梯。需要n阶你才能到达楼顶。

每次你可以爬12个台阶。你有多少种不同的方法可以爬到楼顶呢?

示例 1
输入: n = 2
输出: 2
解释: 有两种方法可以爬到楼顶。

  1. 1 阶 + 1 阶
  2. 2 阶

示例 2
输入: n = 3
输出: 3
解释: 有三种方法可以爬到楼顶。

  1. 1 阶 + 1 阶 + 1 阶
  2. 1 阶 + 2 阶
  3. 2 阶 + 1 阶

提示

  • 1 <= n <= 45

相关主题

  • 记忆化搜索
  • 数学
  • 动态规划

二、题解

方法 1: 动态规划

pub fn climb_stairs(n: i32) -> i32 {
    let (mut p, mut q, mut r) = (0, 0, 1);

    for _ in 0..n {
        p = q;
        q = r;
        r = p + q;
    }

    r
}

方法 2: 矩阵快速幂

pub fn climb_stairs(mut n: i32) -> i32 {
    let mut m = vec![vec![1, 1], vec![1, 0]];

    let multiply = |a: &[Vec<i64>], b: &[Vec<i64>]| {
        let mut c = vec![vec![0; 2]; 2];
        for i in 0..2 {
            for j in 0..2 {
                c[i][j] = a[i][0] * b[0][j] + a[i][1] * b[1][j];
            }
        }
        c
    };
    let mut pow = || {
        let mut ret = vec![vec![1, 0], vec![0, 1]];
        while n > 0 {
            if n & 1 == 1 {
                ret = multiply(&ret, &m);
            }
            n >>= 1;
            m = multiply(&m, &m);
        }
        ret
    };

    let res = pow();

    res[0][0] as i32
}

方法 3: 通项公式

pub fn climb_stairs(n: i32) -> i32 {
    let sqrt5 = 5_f64.sqrt();
    let fib_n = ((1.0 + sqrt5) / 2.0).powi(n + 1) - ((1.0 - sqrt5) / 2.0).powi(n + 1);

    return (fib_n / sqrt5).round() as i32;
}

方法 4: 排列组合

pub fn climb_stairs(n: i32) -> i32 {
    let n = n as i64;
    let calc = |mut a, b| {
        let mut ans = 1;
        for i in 1..=b {
            ans *= a;
            a -= 1;
            ans /= i;
        }
        ans
    };

    let mut res = 1;
    for i in 1..=n / 2 {
        res += calc(n - i, i);
    }

    res as i32
}