【剑指offer】骰子的点数

骰子的点数

题目描述

将一个骰子投掷 n
次,获得的总点数为 s
,s
的可能范围为 n∼6n

掷出某一点数,可能有多种掷法,例如投掷 2
次,掷出 3
点,共有 [1,2],[2,1]
两种掷法。

请求出投掷 n次,掷出 n∼6n点分别有多少种掷法。

数据范围:1≤n≤10
样例1
输入:n=1
输出:[1, 1, 1, 1, 1, 1]
解释:投掷1次,可能出现的点数为1-6,共计6种。每种点数都只有1种掷法。所以输出[1, 1, 1, 1, 1, 1]。

样例2
输入:n=2
输出:[1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]
解释:投掷2次,可能出现的点数为2-12,共计11种。每种点数可能掷法数目分别为1,2,3,4,5,6,5,4,3,2,1。
所以输出[1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]。

思路

DP分析主要流程:

  • 状态表示
  • 状态计算

状态表示:按照题目来, 本题要求投掷n次,掷出n~6n点分别有多少种掷法。因此是两维状态,f[i][j]表示投掷i次,掷出j点数有多少种掷法。
状态计算:这个地方有点复杂, 主要是寻找变化和不变化的部分

现在思考这么个场景
有一个问卷调查, 一共n道题目每个题目都是判断题, 你只需要填true或false
如果直接求会有些复杂(假设你不会公式)

那么你需要敢肯定的是:

  1. 乘法原理, 每次的结果相乘即可
  2. 并且最后一次不是true就是false

那么你就知道f[n] = true或者f[n] = false;
而中间的某次, 肯定也是这样, 也就是f[i] = true或者f[i] = false;

如果你能想明白,恭喜你已经会本题了
显然,每次扔骰子,只有1~6种方案,上面的例子是乘法原理,本题求的是方案数,是加法原理
并且,投n次,总和为s
那么中间的某次肯定是投了n - i次,总和为6 * n - k
因此,状态计算就是把这些中间状态利用加法原理进行累加即可

代码

i:投了多少次
j: 投n次的情况下,总和是多少
k: 每次的点数是多少

class Solution {
public:
    vector<int> numberOfDice(int n) {
        vector<vector<int>> f(n + 1, vector<int>(n * 6 + 1));
       
       f[0][0] = 1;
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= i * 6; j ++ )
                for (int k = 1; k <= min(j, 6); k ++ )      // 注意投的点数要小于总和
                    f[i][j] += f[i - 1][j - k];
        
        vector<int> res;
        for (int i = n; i <= n * 6; i ++ ) 
            res.push_back(f[n][i]);
        
        return res;
    }
};

易错点

  • 第一层循环为枚举投的次数,第二层循环为每次总和,因此j的哨兵为:j <= i * 6
  • 状态转移: 最后一次投了k, 那么之前投的状态为:f[i - 1][j - k],表示投的次数少了一次,并且总和减少了k
  • 答案循环,答案需要投N次的总和,因此f的第一维固定为n,第二位是总和,因为投了n次,所以总和为n ~ n * 6
  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值