leetcode837-新21点

原题

爱丽丝参与一个大致基于纸牌游戏 “21点” 规则的游戏,描述如下:

爱丽丝以 0 分开始,并在她的得分少于 K 分时抽取数字。 抽取时,她从 [1, W] 的范围中随机获得一个整数作为分数进行累计,其中 W 是整数。 每次抽取都是独立的,其结果具有相同的概率。

当爱丽丝获得不少于 K 分时,她就停止抽取数字。 爱丽丝的分数不超过 N 的概率是多少?

示例 1:

输入: N = 10, K = 1, W = 10
输出: 1.00000
说明: 爱丽丝得到一张卡,然后停止。

示例 2:

输入: N = 6, K = 1, W = 10
输出: 0.60000
说明: 爱丽丝得到一张卡,然后停止。
在 W = 10 的 6 种可能下,她的得分不超过 N = 6 分。

示例 3:

输入: N = 21, K = 17, W = 10
输出: 0.73278

提示:

  1. 0 <= K <= N <= 10000
  2. 1 <= W <= 10000
  3. 如果答案与正确答案的误差不超过 10^-5,则该答案将被视为正确答案通过。
  4. 此问题的判断限制时间已经减少。

解法

思想

可以从dfs的角度入手,这是自顶向下的解法,但比较耗时,

于是可以转换为动态规划。

代码

未经优化的DFS,计算了很多重复单元:

class Solution {
    int K;
    int W;
    int N;
    public double new21Game(int N, int K, int W) {
        this.K = K;
        this.W = W;
        this.N = N;
        return dfs(0,1.0);
    }

    public double dfs(int cur,double p){
        if(cur>=K){
            if(cur<=N){
                return p;
            }
            return 0.0;
        }
        double ans = 0.0;
        for(int i = 1;i<=W;i++){
            ans += dfs(cur+i,p/W);
        }
        return ans;
    }
}

加入记忆化后的DFS,仍然超时:

class Solution {
    int K;
    int W;
    int N;
    Double[][] memory;
    public double new21Game(int N, int K, int W) {
        this.K = K;
        this.W = W;
        this.N = N;
        memory = new Double[K][K];
        return dfs(0,0);
    }

    public double dfs(int cur,int depth){
        if(cur>=K){
            if(cur<=N){
                return Math.pow(1.0/W,depth);
            }
            return 0.0;
        }
        if(memory[cur][depth]!=null) return memory[cur][depth];
        double ans = 0.0;
        for(int i = 1;i<=W;i++){
            ans += dfs(cur+i,depth+1);
        }
        memory[cur][depth] = ans;
        return ans;
    }
}

那么需要进一步优化,通过分析发现,这里的概率其实仅和cur有关,即memory[cur][depth] == Math.pow(1.0/W,depth) * memory[cur][0],因此只需要计算出memory[0][0],memory[1][0],memory[2][0]...就能完成大部分计算。

于是可以使用动态规划来做(用dfs会特别麻烦):

class Solution {
    public double new21Game(int N, int K, int W) {
        if (K == 0) {
            return 1.0;
        }
        double[] dp = new double[K + W];
        for (int i = K; i <= N && i < K + W; i++) {
            dp[i] = 1.0;
        }
        dp[K - 1] = 1.0 * Math.min(N - K + 1, W) / W;
        for (int i = K - 2; i >= 0; i--) {
            dp[i] = dp[i + 1] - (dp[i + W + 1] - dp[i + 1]) / W;
        }
        return dp[0];
    }
}

原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/leetcode837-%e6%96%b021%e7%82%b9/

发表评论

电子邮件地址不会被公开。