文章目录
- 题目描述
- 代码 & 思路
- 1. 二维数组(方便理解)
- 2. 一维数组(节约空间)
- 二刷
鸽了好久的打题博客~要继续补起来了!
今天不打题,明天变垃圾 QAQ
题目描述
- 一眼就想先暴力枚举、或者递归呀~但是貌似会超时,这里就直接用dp了
- 参考题解
- 主要思路有点像跳台阶,也就是用上一轮次的和,来维护当前轮次的值
代码 & 思路
1. 二维数组(方便理解)
- 举个例子吧:两个骰子得出来的值8,相当于:
- 一个骰子的2,再补上另一个的6
- 一个骰子的3,再补上另一个的5
- 一个骰子的4,再补上另一个的4
- 一个骰子的5,再补上另一个的3
- 一个骰子的6,再补上另一个的2
- 当然,一个骰子的1,再补上另一个的7 这种情况是不存在的,所以只有5种情况可行。
也就是说 dp[2][8] = dp[1][2] + dp[1][3] + dp[1][4] + dp[1][5]+ dp[1][6]
- 按照这么一个思路,我们就可以得出下面的代码:
class Solution {public double[] dicesProbability(int n) {// 第一行、最后一列舍弃,方便下标理解// dp[骰子数][可投出点数] = 对应次数double[][] dp = new double[n + 1][6 * n + 1];// 初始化for(int i = 1; i <= 6; i++) {dp[1][i] = 1;}// dp 过程for(int i = 2; i <= n; i++) {// 范围[i, 6 * i]for(int j = i; j <= 6 * i; j++) {// 取上一层的前6个for(int k = 1; k <= 6; k++) {// 取不到6个则有多少取多少if(j - k <= 0) {break;}// 状态转移方程 dp[i][j] += dp[i - 1][j - k];}}}// 结束,取答案double all = Math.pow(6, n);double[] ans = new double[5 * n + 1];for(int i = 0; i < ans.length; i++) {ans[i] = dp[n][i + n] / all;}return ans;}
}
2. 一维数组(节约空间)
- 在上一个方法的基础上,我们知道:第 n 轮的值,只和第 n - 1 轮的值相关
- (还是得去上面链接的题解结合动图理解)
- 经过思考,可以发现其实按照从后往前的顺序进行数组更新,就只需要一维数组即可:
- 举个例子(实际还是1维,这里的行值是辅助了解当前状态):
- dp[2][12] = dp[1][11] + dp[1][10] + … + dp[1][6]
- dp[2][11] = dp[1][10] + dp[1][9] + … + dp[1][5]
- …
- dp[2][2] = dp[1][1]
- 这也就是需要从后往前的原因,如果从前往后的话,dp[2][12]就取不到dp[1][11]的值了。从后往前可以实现无后效性~
- 那么进行一个空间上的优化,可以得到以下代码:
class Solution {public double[] dicesProbability(int n) {// 0 下标不取,一共 6 * n 个格子double[] dp = new double[6 * n + 1];// 初始化,全为1for(int i = 1; i <= 6; i++) {dp[i] = 1;}// 2个、3个...n个骰子,以此类推for(int i = 2; i <= n; i++) {// 从后往前,可推n个骰子范围[n, 6 * n]for(int j = 6 * i; j >= i; j--) {// 给新的格子赋值dp[j] = 0;// 获取前六个格子的值(类似走楼梯)for(int k = 1; k <= 6; k++) {// 过于倒退的情况,结束(不一定都能取6个)if(j - k < i - 1) {break;}// 累加前面的格子值dp[j] += dp[j - k];}}}// 获取答案double all = Math.pow(6, n);double[] ans = new double[5 * n + 1];for(int i = 0; i < ans.length; i++) {ans[i] = dp[n + i] / all;}return ans;}
}
二刷
- O(n2n^2n2)、O(n2n^2n2)
class Solution {public double[] dicesProbability(int n) {// 1. dp初始化int[][] ans = new int[n + 1][6 * n + 1];for(int i = 1; i <= 6; i++) {ans[1][i] = 1;}// 2. dp 过程 O(n^2)for(int i = 2; i <= n; i++) {for(int j = i; j <= i * 6; j++) {for(int k = 1; k <= 6 && j - k >= i - 1; k++) {ans[i][j] += ans[i - 1][j - k];}}}// 3. 获取答案double[] myAns = new double[5 * n + 1];for(int i = 0; i < myAns.length; i++) {myAns[i] = ans[n][i + n] / Math.pow(6, n);;}return myAns;}
}
- O(n2n^2n2)、O(nnn)
class Solution {public double[] dicesProbability(int n) {// 1. dp初始化int[] ans = new int[6 * n + 1];for(int i = 1; i <= 6; i++) {ans[i] = 1;}// 2. dp 过程 O(n^2)for(int i = 2; i <= n; i++) {for(int j = 6 * i; j >= i; j--) {ans[j] = 0;for(int k = 1; k <= 6 && j - k >= i - 1; k++) {ans[j] += ans[j - k];}}}// 3. 获取答案double[] myAns = new double[5 * n + 1];for(int i = 0; i < myAns.length; i++) {myAns[i] = ans[i + n] / Math.pow(6, n);;}return myAns;}
}