从零学算法(LCR 185. 统计结果概率)

你选择掷出 num 个色子,请返回所有点数总和的概率。
你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 num 个骰子所能掷出的点数集合中第 i 小的那个的概率。
示例 1:
输入:num = 3
输出:[0.00463,0.01389,0.02778,0.04630,0.06944,0.09722,0.11574,0.12500,0.12500,0.11574,0.09722,0.06944,0.04630,0.02778,0.01389,0.00463]
示例 2:
输入:num = 5
输出:[0.00013,0.00064,0.00193,0.00450,0.00900,0.01620,0.02636,0.03922,0.05401,0.06944,0.08372,0.09452,0.10031,

  • 首先读懂题目,其实题目意思就是给你 n 个色子,按顺序求出从点数 n 到 6*n 分别的概率。这是怎么想到动态规划的呢?首先假设 f(n) 为 n 个色子的概率列表,f(n,x) 为投掷 n 个色子,点数之和为 x 的概率。接下来我们看看 f(n-1) 和 f(n) 有没有联系,你会发现,当增加一个色子,因为它只可能为 1-6,所以 f(n,x) 其实有 6 个到来的途径,等于 f(n-1,x-1)*(1/6)+f(n-1,x-2)*(1/6)+...+f(n-1,x-6)*(1/6)。形象的说就是比如我 5 个色子要掷出 9 点即 f(5,9),我可能是前四个色子有 8 点,第五个色子为一点(一个色子为一点的可能性为 1/6),即 f(4,8)*(1/6),同理可能之前为 7 点,现在 2 点…之前 3 点,现在 9 点。
  • 但是,这里有个问题,如果我 5 个色子掷出了 8 点,难不成你还能是之前四个色子掷出 3 点,现在 5 点吗?四个色子最少 4 点啊。所以这里我们逆向思维,我们不从 f(n,x) 去寻找可能的 f(n-1,y),我们从 f(n-1,x) 正向去推导,它可能会对之后的哪 6 种可能产生影响,即从f(n-1,x) 推导出 f(n,x+1),f(n,x+2)…f(n,x+6)。发现了吗,这正好符合我们的动态规划,因为我们知道 f(1) 就是 6 个 1/6。我们从 f(1) 推到 f(n) 岂不正好。
  • 这里我们就不用二维数组了,因为 f(n) 只和 f(n-1) 有关,所以我们用两个数组交替迭代即可。
  •   public double[] statisticsProbability(int n) {
          double[] dp = new double[6];
          Arrays.fill(dp, 1.0 / 6.0);
          for (int i = 2; i <= n; i++) {
              double[] tmp = new double[5 * i + 1];
              // 遍历 f(n-1,x)
              for (int j = 0; j < dp.length; j++) {
              	// 推导之后的 f(n,x+1),f(n,x+2)...f(n,x+6)
                  for (int k = 0; k < 6; k++){
                      tmp[j+k] += dp[j]/6.0;
                  }
              }
              dp=tmp;
          }
          return dp;
      }
    

你可能感兴趣的:(算法学习,#,动态规划,算法)