leetcode投骰子问题动态规划

题目描述

有一个骰子模拟器会每次投掷的时候生成一个 1 到 6 的随机数。

不过我们在使用它时有个约束,就是使得投掷骰子时,连续 掷出数字 i 的次数不能超过 rollMax[i]i 从 1 开始编号)。

现在,给你一个整数数组 rollMax 和一个整数 n,请你来计算掷 n 次骰子可得到的不同点数序列的数量。

假如两个序列中至少存在一个元素不同,就认为这两个序列是不同的。由于答案可能很大,所以请返回 模 10^9 + 7 之后的结果。

示例 1:

输入:n = 2, rollMax = [1,1,2,2,2,3]
输出:34
解释:我们掷 2 次骰子,如果没有约束的话,共有 6 * 6 = 36 种可能的组合。但是根据 rollMax 数组,数字 1 和 2 最多连续出现一次,所以不会出现序列 (1,1) 和 (2,2)。因此,最终答案是 36-2 = 34。

示例 2:

输入:n = 2, rollMax = [1,1,1,1,1,1]
输出:30

示例 3:

输入:n = 3, rollMax = [1,1,1,2,2,3]
输出:181

 

提示:

  • 1 <= n <= 5000
  • rollMax.length == 6
  • 1 <= rollMax[i] <= 15

思路

用一个三维数组d[i][j][k](i<=n,j<=6,k<=rollMax[j])表示第i次枚举,第j个面连续出现k次的解的数量,即以k个j结尾的数列的情况总数。

递推关系如下:

情况1:这一层的面和上一层相同,当前d[i][j][t]+=d[i-1][j][t-1],t表示j到当前层位置连续出现的次数,2≤t≤rollMax[j]。

情况2:这一层的面和上一层不同,当前d[i][j][1]+=d[i-1][last][lasttimes],last代表上一次出现的数字,lasttimes代表上一次出现的数字连续出现的次数

代码

//用一个三维数组d[i][j][k],表示第i次(1-n)枚举,第j个面(1-6),连续出现k次的解的数量,也就是以k个j结尾的数列的情况总数
class Solution {
public:
    int dieSimulator(int n, vector& rollMax) {
        int res=0;
        int mod=1000000007;
        int d[n+1][7][16]={0};
        //要给第一次枚举赋值
        for(int i=1;i<=6;i++) d[1][i][1]=1;
        //第i次枚举
        for(int i=1;i<=n;i++){
            //骰子的六个面
            for(int j=1;j<=6;j++){
                //上一次出现的数枚举
                for(int last=1;last<=6;last++){
                    //这次的数和上一次一样,重复了
                    if(last==j){
                        //本层至少出现两次,计数从2开始
                        for(int lasttimes=2;lasttimes<=rollMax[last-1];lasttimes++){
                            d[i][j][lasttimes]=(d[i][j][lasttimes]+d[i-1][last][lasttimes-1])%mod;
//这里保证了数组中每隔值都不超过mod
                        }
                    }
                    //这一次的数和上一次不一样,第一次出现
                    else{
                        //上一次出现的数可能出现1次两次...到max次
                        for(int lasttimes=1;lasttimes<=rollMax[last-1];lasttimes++){
                            d[i][j][1]=(d[i][j][1]+d[i-1][last][lasttimes])%mod;
                        }
                    }
                }
            }
        }
        for(int i=1;i<=6;i++){
            for(int j=1;j<=rollMax[i-1];j++){//每一个面出现的次数最多为rollMax中规定的最大值
                res=(res+d[n][i][j])%mod;
            }
        }
        return int(res);
    } 
};

总结

这道题本来的想法是用排列组合,先用6^n算出所有可能的情况,然后再减去不可能的情况数量,后来发现,n的最大值是5000,6^5000过大,而且计算不可能的情况的过程有些麻烦,就放弃了这个思路。

发现这类需要枚举的问题适合用多维数组去解决,让多维数组的每个维度对应问题中的一个有意义的值。多维数组迭代到最后计算出符合条件的解(第n层)。

ps这道题也说明结果值很大的题不一定需要用到long long,通过合适地方的取余运算,int类型就可以解决。

你可能感兴趣的:(#动态规划,leetcode)