有一个骰子模拟器会每次投掷的时候生成一个 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类型就可以解决。