LeetCode每日一题:3154 到达第 K 级台阶的方案数

题目

给你有一个 非负 整数 k 。有一个无限长度的台阶,最低 一层编号为 0 。

Alice 有一个整数 jump ,一开始值为 0 。Alice 从台阶 1 开始,可以使用 任意 次操作,目标是到达第 k 级台阶。假设 Alice 位于台阶 i ,一次 操作 中,Alice 可以:

  • 向下走一级到 i - 1 ,但该操作 不能 连续使用,如果在台阶第 0 级也不能使用。
  • 向上走到台阶 i + 2^jump 处,然后 jump 变为 jump + 1 。

请你返回 Alice 到达台阶 k 处的总方案数。

注意,Alice 可能到达台阶 k 处后,通过一些操作重新回到台阶 k 处,这视为不同的方案。

示例 1:

输入:k = 0

输出:2

解释:

2 种到达台阶 0 的方案为:

  • Alice 从台阶 1 开始。
    • 执行第一种操作,从台阶 1 向下走到台阶 0 。
  • Alice 从台阶 1 开始。
    • 执行第一种操作,从台阶 1 向下走到台阶 0 。
    • 执行第二种操作,向上走 20 级台阶到台阶 1 。
    • 执行第一种操作,从台阶 1 向下走到台阶 0 。

示例 2:

输入:k = 1

输出:4

解释:

4 种到达台阶 1 的方案为:

  • Alice 从台阶 1 开始,已经到达台阶 1 。
  • Alice 从台阶 1 开始。
    • 执行第一种操作,从台阶 1 向下走到台阶 0 。
    • 执行第二种操作,向上走 20 级台阶到台阶 1 。
  • Alice 从台阶 1 开始。
    • 执行第二种操作,向上走 20 级台阶到台阶 2 。
    • 执行第一种操作,向下走 1 级台阶到台阶 1 。
  • Alice 从台阶 1 开始。
    • 执行第一种操作,从台阶 1 向下走到台阶 0 。
    • 执行第二种操作,向上走 20 级台阶到台阶 1 。
    • 执行第一种操作,向下走 1 级台阶到台阶 0 。
    • 执行第二种操作,向上走 21 级台阶到台阶 2 。
    • 执行第一种操作,向下走 1 级台阶到台阶 1 。

思路与分析

        这个和阶梯问题很相似,最先的想法是递归,但因为jump会变,有可能到达同一个台阶会经历不同的jump,递推公式可能有点复杂。所以要另外找其他的方法。

        向下走每次只能走1,可以视为对当前位置的微调,向上走是指数次的增加,所以当k很大的时候,决定能不能到达的因素应该是此时的jump。所以要找到每次jump和这次jump能不能到达k之间的关系。

        对应于每一次的jump,能到达的最高的阶梯就是从不后退,而最低的阶梯就是穿插着一直后退,考虑到起始位置在x=1的地方,所以每次jump能到达的最高位置如下(1是起始下标)

1+2^0+2^1+...+2^j=2^{j+1}

        最低位置是一样的,只要在每两次jump之间插入一个-1即可,每次jump到达的最低位置就是2^{j+1}-(j+2),在这个范围内只需要控制-1的次数就可以做到到达任意一级台阶。于是问题就转化成了组合问题。

        而对于第k级台阶,样例中可以看到在不同jump的情况下都有可能达到,所以对于输入的k要看在不在多个jump的区间范围内,基于此代码就很清晰了。

具体实现

        首先做个可以计算阶乘的函数

int comb(int n, int r) {
    if (r == 0 || r == n) return 1;
    r = r < n-r ? r : n-r;
    long long result = 1;
    for (int i = 0; i < r; ++i) {
        result = result * (n - i) / (i + 1);
    }
    return result;
} 

        之后去算每次jump区间内是否有k,如果有的话需要退后几次,计算一个组合数找到全部退后的组合数就可以了。

        求二的阶乘可以直接用位运算的方法,更快一点

int waysToReachStair(int k) {
    long long int res = 0, jump = 0;
    while ((1 << jump) - (jump + 1) <= k) {
        if ((1 << jump) >= k) {
            res += comb(jump + 1, (1 << jump) - k);
        }
        jump++;
    }
    return res;
}

        全部的代码如下:

class Solution {
public:
    int comb(int n, int r) {
        if (r == 0 || r == n) return 1;
        r = r < n-r ? r : n-r;
        long long result = 1;
        for (int i = 0; i < r; ++i) {
            result = result * (n - i) / (i + 1);
        }
        return result;}  
    int waysToReachStair(int k) {
        long long int res = 0, jump = 0;
        while ((1 << jump) - (jump + 1) <= k) {
            if ((1 << jump) >= k) {
                res += comb(jump + 1, (1 << jump) - k);
            }
            jump++;
        }
        return res;
    }
};

后话

        又要开一个新坑喽~,用来记一下每天刷过的算法题,非科班畜生做这些题收获还是蛮多的,希望自己能坚持下来吧!

  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值