You have d dice, and each die has f faces numbered 1, 2, ..., f. Return the number of possible ways (out of fd total ways) modulo 10^9 + 7 to roll the dice so the sum of the face up numbers equals target.
- 传送门:原题
1.思路 & 解法
从tag来看动态规划DP(dynamic programming)问题,什么样的问题可以用动态规划解?简单而言,如果f(n) = A1 * f(n-k1) + A2 * f(n-k2)+....+Am * f(n-km) + C,A1、A2、C均为常数则可用动态规划解;
对于此题而言,我们由常识可知,
"d个f面骰子组成target"="d-1个骰子组成target-1"+"d-1个骰子组成target-2"+"..."+"d-1个骰子组成target-f",
即递推公式:f(d,target) = f(d-1,target-1) + f(d-1,target-2) + .... + f(d-1,target-f);
显然,f(1,1) = 1、f(1,2) = 1、 .... 、f(1,f) = 1;
因此,我们可以设计一个宽f*d高d 的二维数组,将第一行的数据f(1,1)、f(1,2)、...、f(1,f)设置为1,
则第二行、第三行、....、第n行,均可由公式得出。
而f(d,target)为我们所需。
代码如下:
int numRollsToTarget(int d, int f, int target){
if (d <= 0 || f <= 0 || d * f < target || d > target) {
return 0;
}
int dp_array[d][f * d];
memset(dp_array, 0, sizeof(int) * d * f * d);
for (int i = 0; i < f; i++) {
dp_array[0][i] = 1;
}
int mod_number = pow(10, 9) + 7;
for (int i = 1; i < d; i++) {
for (int j = i; j < i * f + f && j < target; j++) {
int sum = 0;
//注意边界 k >= 0
for (int k = j - 1; k >= 0 && k >= j - f; k--) {
sum += dp_array[i - 1][k];
sum = sum % mod_number;
}
dp_array[i][j] = sum;
}
}
return dp_array[d-1][target-1];
}
2. 优化
显然,对于这里的递推公式,f(d,target)用不到任何d - 2的结果项f(d-2,m),那么意味着有 有 (d - 2) * d * f的空间浪费了,我们来优化一下
代码:
int numRollsToTarget(int d, int f, int target){
if (d <= 0 || f <= 0 || d * f < target || d > target) {
return 0;
}
int size = d * f;
int stack1[size];
int stack2[size];
memset(stack1, 0, sizeof(int) * size);
memset(stack2, 0, sizeof(int) * size);
for (int i = 0; i < f; i++) {
stack1[i] = 1;
}
int mod_number = pow(10, 9) + 7;
for (int i = 1; i < d; i++) {
//处理一下不可能项f(d,d-1)
stack2[i - 1] = 0;
for (int j = i; j < i * f + f && j < target; j++) {
int sum = 0;
//注意边界 k >= 0
for (int k = j - 1; k >= 0 && k >= j - f; k--) {
sum += stack1[k];
sum = sum % mod_number;
}
stack2[j] = sum;
}
memcpy(stack1, stack2, sizeof(int) * size);
}
return stack1[target - 1];
}