剑指offer43——n个骰子的点数

本文探讨了如何计算n个骰子投掷后得到特定点数和的概率,通过递归和动态规划方法解决了问题,避免了重复计算,并优化了算法效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述
类似青蛙跳台阶的问题
第n个骰子点数为1的话,f(n,s)=f(n-1,s-1),当第n个骰子点数为2的话,f(n,s)=f(n-1,s-2),…,
依次类推。在n-1个骰子的基础上,再增加一个骰子出现点数和为s的结果只有这6种情况!
f(n,s)=f(n-1,s-1) 这个等式只是在 第n个点数为1的时候,所有考虑的全部的情况,那就是 6种情况全部考虑进去

static int getNumCount(int n, int sum) {
    // n为骰子数,sum,n个骰子数的大小
    // 3种越界的情况
    if (n < 1 || n > sum || sum > 6 * n) {
        return -1;
    }
    // 1个骰子的话,所有的情况只有1种可能
    if (n == 1) {
        return 1;
    }
    // 类似青蛙跳台阶的问题
    // 第n个骰子点数为1的话,f(n,s)=f(n-1,s-1),当第n个骰子点数为2的话,f(n,s)=f(n-1,s-2),…,
    // 依次类推。在n-1个骰子的基础上,再增加一个骰子出现点数和为s的结果只有这6种情况!
    // f(n,s)=f(n-1,s-1) 这个等式只是在 第n个点数为1的时候,所有考虑的全部的情况,那就是 6种情况全部考虑进去
    return getNumCount(n - 1, sum - 1) + getNumCount(n - 1, sum - 2) + getNumCount(n - 1, sum - 3)
            + getNumCount(n - 1, sum - 4) + getNumCount(n - 1, sum - 5) + getNumCount(n - 1, sum - 6);
}
public static void main(String[] args) {
    // 假定值
    int n = 5;
    int[] count = new int[6 * n + 1];
    for (int i = n; i <= 6 * n; i++) {
        count[n] = getNumCount(n,i);
    }
}

递归的方法就会发生 某一个n和sum的组合被多次的计算,所以我们需要对他进行剪枝。
剪枝的方法一般是通过空间来换取时间,所以我们使用额外空间来保存计算过的结果。

使用二维数组,空间复杂度还是有点高。

static int[][] getNumCount1(int n) {
    int[][] count = new int[n + 1][6 * n + 1];
    count[1][1] = count[1][2] = count[1][3] = count[1][4] = count[1][5] = count[1][6] = 1;
    for (int i = 2; i <= n; i++) {
        for (int j = i; j <= 6 * i; j++) {
            int temp1 = j - 1 < 1 ? 0 : count[i - 1][j - 1];
            int temp2 = j - 2 < 1 ? 0 : count[i - 1][j - 2];
            int temp3 = j - 3 < 1 ? 0 : count[i - 1][j - 3];
            int temp4 = j - 4 < 1 ? 0 : count[i - 1][j - 4];
            int temp5 = j - 5 < 1 ? 0 : count[i - 1][j - 5];
            int temp6 = j - 6 < 1 ? 0 : count[i - 1][j - 6];
            count[i][j] = temp1 + temp2 + temp3 + temp4 + temp5 + temp6;
        }
    }
    return count;
}

一维数组

 /****************************************
     func:给定骰子数目n,求所有可能点数和的种类数
     para:n:骰子个数;count:存放各种点数和的种类数,下标i表示点数和为(i+n)
     ****************************************/
static int[] getNumCount2(int n) {
    if (n < 1)
        return null;
    int[] count = new int[5*n+1];
    //初始化最初状态
    count[0] = count[1] = count[2] = count[3] = count[4] = count[5] = 1;
    if (n == 1) return null;
    for (int i = 2; i <= n; ++i) {
        for (int sum = 6 * i; sum >= i; --sum) {
            int tmp1 = ((sum - 1 - (i - 1)) >= 0 ? count[sum - 1 - (i - 1)] : 0); //上一阶段点数和sum-1的排列总数
            int tmp2 = (sum - 2 - (i - 1) >= 0 ? count[sum - 2 - (i - 1)] : 0);
            int tmp3 = (sum - 3 - (i - 1) >= 0 ? count[sum - 3 - (i - 1)] : 0);
            int tmp4 = (sum - 4 - (i - 1) >= 0 ? count[sum - 4 - (i - 1)] : 0);
            int tmp5 = (sum - 5 - (i - 1) >= 0 ? count[sum - 5 - (i - 1)] : 0);
            int tmp6 = (sum - 6 - (i - 1) >= 0 ? count[sum - 6 - (i - 1)] : 0);
            count[sum - i] = tmp1 + tmp2 + tmp3 + tmp4 + tmp5 + tmp6;
        }
    }
    return count;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值