Leetcode837

18 篇文章 0 订阅
12 篇文章 0 订阅

参考了:https://zhuanlan.zhihu.com/p/86167813

 

题意:初始是0点,每次可以从[1, W]中随机出一个数字,累加上去,直到自己的点数 > K, 求这个时候点数 <= N的概率

 

我们使用动态规划来做:

dp[i]表示我们达到点数为i的概率,注意到当 i >= K + W的时候 , dp[i] = 0, 

考虑dp[i]怎么求,点数i可以由i-1加1得到,可以由i-2加2得到等等

dp[i] = dp[i-1] * 1/W + dp[i-2] * 1/W + ... + dp[i-W] * 1/W

实际上呢这个式子是有一定的使用条件的,以题目中的

N = 21, K = 17, W = 10

来举例,当i=12的时候,是没问题的

dp[12] = 1/10(dp[11] + dp[10] + .. dp[2])

 

当i比较小的时候 dp[3] =  1/10(dp[2] + dp[1] +dp[0]), 也就是说当i比较小的时候,后面的项比较少

当i大于K的时候呢?比如i=20

注意这个时候20是不能从19+1得到的,因此19的时候已经 19 > 17=K了,我们只能从最大是K-1的递推过来

dp[20] = 1/10*(dp[16] +dp[15] +...+ dp[10])

 

题目要求的是什么呢?

是在点数X>=K的时候X<=N的概率,这是一个条件概率,即P(X<=N | X>=K)

根据贝叶斯公式:

P(X<=N | X>=K) = P(X<=N  and  X>=K) / P(X >= K)

分子是P(K) + P(K+1) + ...+P(N)

分母是P(K) + P(K+1) +...+P(K+W-1),因为i >= K + W的时候 , P(i)为0

 

下面是一个实现的代码:

    public double new21Game(int N, int K, int W) {
        double[] d = new double[K + W ];
        d[0] = 1;
        for (int i = 1; i <= K; i++) {
            double s = 0;
            for (int j = 1; j <= W; j++) {
                if (i - j < 0) break;
                s += d[i - j];
            }
            d[i] = s / W;
        }

        for (int i = K + 1; i <= K + W - 1; i++) {
            double s = 0;
            for (int j = K - 1; j >= 0; j--) {
                int dif = i - j;
                if (dif > W) break;
                s += d[j];
            }
            d[i] = s / W;
        }

        double fenzi = 0;
        double fenmu = 0;
        for (int i = K; i <= K + W - 1; i++) fenmu += d[i];
        for (int i = K; i <= Math.min(N, K + W - 1); i++) fenzi += d[i];
        return fenzi / fenmu;
    }

由于N, K<=10000, 这个O((N+K)^2)是会超时的,使用前缀和优化到O(N+K)


    public double new21Game(int N, int K, int W) {
        double[] d = new double[K + W];
        double[] sum = new double[K + W];
        d[0] = 1;
        sum[0] = 1;
        for (int i = 1; i <= K; i++) {
            double s;
            if (i <= W) s = sum[i - 1];
            else s = sum[i - 1] - sum[i - 1 - W];
            d[i] = s / W;
            sum[i] = sum[i - 1] + d[i];
        }

        for (int i = K + 1; i <= K + W - 1; i++) {
            int upBound = K - 1;
            double sup = upBound >= 0 ? sum[upBound] : 0;
            int lowBound = (i - W - 1);
            double s = sup - (lowBound >= 0 ? sum[lowBound] : 0);
            d[i] = s / W;
            sum[i] = sum[i - 1] + d[i];
        }

        double fenzi = 0;
        double fenmu = 0;
        for (int i = K; i <= K + W - 1; i++) fenmu += d[i];
        for (int i = K; i <= Math.min(N, K + W - 1); i++) fenzi += d[i];
        return fenzi / fenmu;
    }

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值