上台阶问题——利用高中排列组合知识

本文探讨了一种解决上台阶问题的动态规划算法,通过约分和利用排列组合对称性减少数据溢出。作者创建了一个Steps类,实现了getOptions()、getU()和getD()方法来计算不同台阶数的走法。在计算过程中,通过判断能否整除7的阶层进行约分,同时利用不等式优化计算,有效避免了数据溢出。文章以4、30、50为例展示了算法的运行结果,并表达了分享个人思路的愿望。
摘要由CSDN通过智能技术生成

问题描述

一共有num节台阶,一次可以上一节或者上两节,问一共有多少种走法
例如:num = 4,则有一下几种情况:

  1. 走四步:一次一节上
  1. 走三步:一次两节+两次一节
  1. 走两步:两次两节

    故上四节楼梯一共有五种走法

思路

若问上五十节台阶有多少种走法

  1. 不采用 “一次上两步” 的走法

    一共要走50步,每步都上一个台阶,所得结果为:
    在这里插入图片描述

  2. 采用一次 “一次上两步” 的走法
    一共要走49步,48步上一个台阶,1步上两个台阶,所得结果为:
    在这里插入图片描述

  3. 采用两次 “一次上两步” 的走法
    一共要走48步,46步上一个台阶,2步上两个台阶,所得结果为:
    在这里插入图片描述

    … … … … … …


    … … … … … …


    … … … … … …

  4. 最多采用50 / 2 = 25次 “一次上两步” 的走法
    一共要走25步,每步都上两个台阶,所得结果为:
    在这里插入图片描述

    综上,把所有情况的累加就是最终答案

规律总结

通过以上分析可得出,如果总台阶数为num,则走法的数量为:

代码实现

创建一个名为Steps的类

num :总台阶数
ok :初始值为0,判断能否约分
getOptions() :获得所有走法的总数量
getUp() : 获得分母
getD() : 获得分子

关键:
因数据过大,主要要解决数据溢出问题,故想办法将数据变小,有两个方面可以减小数据

  1. 约分,在计算分母的getU()方法中,设定当n达到一定大小时(38行,这里定为
    10), 每多乘以一个数,就判断当前值能否整除7的阶层,若可以整除(38行),
    则 将ok的值设为1,并将结果除以7的阶层以达到减小数值防止溢出的作用,然后,
    用ok的值为1作为判条件在getD()方法计算分子时,可直接从8开始算起(54行),
    同时,为防止在getU()方法中的值在后来的计算中又出现可以整除7的阶层的数,
    故用ok的值作为判断 条件,如果ok等于0,则进入,并将OK的值改为1,反过来
    制约下一次碰到整除7的阶层的数导致多除(记得每循环一次后讲ok置零,第14
    行和21行)

  2. 利用排列组合的对称性可得到该公式

    当不等式2b > a-b,即b>( a / 3 )成立时(第9行),可利用该公式,减小运算结果,防止数据溢出

Steps类代码

public class Steps {
    int ok = 0, num;

    public long getOptions(int num) { // num为总台阶数
        long sum = 0, a, b;
        long c;
        this.num = num;
        for (int n = 0; n <= (num / 2); n++) { // n为一次走两步的次数,取值范围从0到 num / 2
            if (n > (num / 3)) {
                a = getU(num - n, num - 2 * n);
                b = getD(num - 2 * n);
                c = a / b;
                sum += c;
                ok = 0;
                continue;
            }
            a = getU(num - n, n);
            b = getD(n);
            c = a / b;
            sum += c;
            ok = 0;
        }
        return sum;
    }

    private long getU(int a, int n) {
        long f = 1;
        int start = a - n + 1;
        if (n == 0 || n == a) {
            return f = 1;
        } else if (n == 1) {
            return f = a;
        }
        for (int i = a; i >= start; i--) {
            
            f = f * i;

            if (n > 10 && f % 5040 == 0 && ok == 0) {
                ok = 1;
                f = f / 5040;
                continue;
            }
        }
        return f;
    }

    private long getD(int n) {
        long f = 1;
        if (n == 0)
            return f = 1;
        else if (n == 1) {
            return f = 1;
        } else if (ok == 1 && n >= 10) {
            for (int i = 8; i <= n; i++) {
                f = f * i;
            }
            return f;
        } else {
            for (int i = 2; i <= n; i++) {
                f = f * i;
            }
            return f;
        }
    }
}

测试类代码

class Text {
    public static void main(String[] args) {
        Steps a = new Steps();
        System.out.println("一共有 " + a.getOptions(50) + " 种走法");
    }
}

运行结果


当台阶数为4时

当台阶数为30时

当台阶数为50时

总结

以上就是我对上台阶问题的想法,可能比递归复杂很多,但是是我自己的想法。
ps:发这篇文章主要是记录记录自己的想法,如果说的有什么不对的地方欢迎大家指正。
初次入坑,请各位大佬多多指教

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值