题目
思路
- 思路一:暴力递归超时,当前求n个筛子能够抛出各个点数的概率,因此用recur(n,i)表示其概率,n表示可用点数,i表示所要求的值,每次递归中抛出的骰子有六种可能k(1,2,3,4,5,6),因遍历recur(n-1,i-k) * 1/6,n-1表示使用了一个骰子,i-k表示剩余要求点数,递归的终止条件就是n==0&&i==0就可结束,其它都算失败结束
- 思路二:备忘录,还是超时
- 思路三:动态规划,就是递归的逆过程,应该属于背包类问题,定义dp[i][j]表示i个骰子中值为j的可能性,转移条件就是dp[i][j] += dp[i - 1][i -k],也就是递归条件,初始化条件就是,当i=1时,也就是骰子个数为1时dp[1][j] = 1/6 | i\j | 0 | 1 | 2 | 3 | 4 | 5 | 6 | | —- | —- | —- | —- | —- | —- | —- | —- | | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | 1 | 0 | 1/6 | 1/6 | 1/6 | 1/6 | 1/6 | 1/6 | | 2 | 0 | 0 | 1/61/6 | 1/61/62 | 1/61/63 | 1/61/64 | 1/61/65 | | 3 | 0 | 0 | 0 | 1/61/61/6 | … | | | | 4 | 0 | 0 | 0 | 0 | 1/61/61/61/6 | … | | | 5 | 0 | 0 | 0 | 0 | 0 | 1/61/61/61/61/6 | …. | | 6 | 0 | 0 | 0 | 0 | 0 | 0 | 1/61/61/61/61/6*1/6 |
-
代码
//1. 暴力递归超时,别直接用0.16667,要用1/6d,不然哭唧唧public double[] dicesProbability(int n) {double[] res = new double[5 * n + 1];for (int i = n; i <= 6 * n; i++) {res[i - n] = recur(n, i);}return res;}public double recur(int n, int target) {if (n == 0 && target == 0) return 1;if (n <= 0 || target < 0) return 0;double res = 0;for (int i = 1; i <= 6; i++) {if (target >= i) {res += recur(n - 1, target - i) * 1/6d;}}return res;}//2.备忘录 还是超时double[][] dp;public double[] dicesProbability(int n) {double[] res = new double[5 * n + 1];dp = new double[n + 1][6 * n + 1];for (int i = n; i <= 6 * n; i++) {res[i - n] = recur(n, i);}return res;}public double recur(int n, int target) {if (n == 0 && target == 0) return 1;if (n <= 0 || target < 0) return 0;if (dp[n][target] != 0) return dp[n][target];double res = 0;for (int i = 1; i <= 6; i++) {if (target >= i) {res += recur(n - 1, target - i) * 1/6d;}}dp[n][target] = res;return res;}//3.动态规划public double[] dicesProbability(int n) {//dp[i][j]表示i个筛子,值为j时的概率double[][] dp = new double[n + 1][6 * n + 1];double[] res = new double[5 * n + 1];for (int j = 1; j <= 6; j++) {dp[1][j] = 1/6d;}for (int i = 1; i <= n; i++) {for (int j = i; j <= 6 * i; j++) {for (int k = 1; k <= 6; k++) {if (j >= k) {dp[i][j] += dp[i - 1][j - k] * 1 /6d;}}if (i == n) {res[j - n] = dp[i][j];}}}return res;}//4. 优化dp数组,由于每次只使用前一行的数据,所以可以优化为一维,但是要有额外一维数组保存新值public double[] dicesProbability(int n) {//dp[j]筛子为i值为j时的概率double[] dp = new double[6 * n + 1];double[] updateDp = new double[6 * n + 1];double[] res = new double[5 * n + 1];for (int j = 1; j <= 6; j++) {dp[j] = 1/6d;res[j - 1] = 1/6d;}for (int i = 2; i <= n; i++) {for (int j = i; j <= 6 * i; j++) {double updateValue = 0;for (int k = 1; k <= 6; k++) {//因为有i个筛子,所以点数至少大于等于iif (j - k + 1 >= i) updateValue += dp[j - k] * 1/6d;}updateDp[j] = updateValue;if (i == n) res[j - n] = updateDp[j];}double[] t = updateDp;updateDp = dp;dp = t;}return res;}
