每日一题(2020-06-03)837. 新21点

[837. 新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. 此问题的判断限制时间已经减少。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/new-21-game

解法:动态规划

根据规则,当分数达到或超过 K 时游戏结束,游戏结束时,如果分数不超过 N 则获胜,如果分数超过 N 则失败。因此当 K ≤ x ≤ min(N, K + W − 1) 时有 dp[x] = 1,当 x > min(N, K + W − 1) 时有 dp[x] = 0

则有状态转移方程为 :

dp[x] = (1 / W) * (dp[x + 1] + dp[x + 2] + ... + dp[x + W])

例如:

class Solution {
    public double new21Game(int N, int K, int W) {
    	if(K == 0) {
    		return 1.0;
    	}
    	double[] dp = new double[K + W];
    	//从 K 到 N 的值为 1
    	for(int i = K; i <= N && i < K + W; i++ ) {
    		dp[i] = 1.0;
    	}
    	for(int i = K - 1; i >= 0; i--) {
    		double temp = 0;
    		// 计算从 dp(x + 1) + dp(x + 2) + ... + dp(x + w)
    		for(int j = 1; j <= W; j++) {
    			temp += dp[i + j];
    		}
    		dp[i] = temp / W;	// 计算从 dp(x) = 1 / W * (dp(x + 1) + dp(x + 2) + ... + dp(x + w))
    	}
    	return dp[0];
    }
}

改进

上述求 dp[x] 的过程中通过循环来求 dp(x + 1) + dp(x + 2) + ... + dp(x + w),而dp[x] 满足下面关系:

  • dp[x] = (1 / W) * (dp[x + 1] + dp[x + 2] + ... + dp[x + W])
  • dp[x - 1] = (1 / W) * (dp[x] + dp[x + 1] + ... + dp[x + W - 1])

dp[x] - dp[x - 1] = (1 / W) * (dp[x + W]) - dp[x])

移项得 dp[x - 1] = dp[x] - (1 / W) * (dp[x + W]) - dp[x])

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 + 1 + W] - dp[i + 1]) / W;
    	}
    	return dp[0];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

两只Tigers跑得快

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值