题目描述
这里有 n 个一样的骰子,每个骰子上都有 k 个面,分别标号为 1 到 k 。
给定三个整数 n , k 和 target ,返回可能的方式(从总共 kn 种方式中)滚动骰子的数量,使正面朝上的数字之和等于 target 。
答案可能很大,你需要对 10^9 + 7 取模 。
做题情况
- 做出来且思路与标答一致
- 做出来但思路较为复杂
- 有思路,但时间复杂度较高无法通过 ☑
- 没有思路
自己的想法:DFS
可能受到了上一道题的影响,一开始考虑的是递归回溯。于是很简单的暴力求解,罗列每一位可以取到的值,然后逐渐递加,知道不能再往上加为止。最终时间远远超过了。
标答:动态规划
二维数组来进行储存,二维分别是到第几个骰子了,以及当前这几个骰子的和为的值。以第几个骰子作为最外层,将上一层的和加上当前骰子可能的值,即可得到包含当前层的结果。
有一个地方需要注意:目标值是有可能小于一个骰子的最大值的,所以在处理累加的时候需要注意一下这一点。
实际代码
class Solution {
public:
int numRollsToTarget(int n, int k, int target)
{
//其实自己就差一点点,动态规划其实二维其实就是骰子数和当前和,两者都没有很大的。
//其实排列还是组合不影响的,我们可以加上限制,比如从前往后的和
vector<vector<long long>> vec(n+1,vector<long long>(target+1,0));
for (int j=1;j<=min(target,k);++j) vec[1][j]=1;
for (int i=2;i<=n;++i)
{
for (int j=1;j<=k;++j)
{
for (int k=0;k<=target;++k)
{
if (j+k<target+1) vec[i][j+k]=(vec[i][j+k]+vec[i-1][k])%1000000007;
}
}
}
return vec[n][target];
}
};
总结
其实还蛮可惜的,思路其实很简单,但自己搞的有点复杂了。
至于动态规划可不可以求正好的问题(而不是说求极值这种),我觉得还是要看情况。如果二维数组中二维设置的比较合理,可以在里面填入数量,然后将判断的部分移到二维设置中。
这个题也蛮典型的,最开始自己尝试的是递归,其实称得上是最慢的方法了。后面改成了动态规划。其实动态规划就是一种遍历的方式,只不过更加有序,就更加快了。
举个例子,第一个投1,第二个投4,和第一个投2,第二个投3没有任何区别,但递归遍历就要计算两种,在动态规划中就可以合并为1种。所以这种细节还是需要注意一下的,这也是让我们找更好方法的思路,即需要在那里优化,就要去找对应的方法。