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
思路:
先读题理清思路。
其中N
代表天花板,所抽牌的总点数超过天花板就是输了;
K
代表抽牌规则,抽牌总点数小于K
的时候必须继续抽,大于等于K
停止抽牌,然后判断输赢;
W
代表每次抽牌的区域[1,W];
什么时候停止抽牌取决于最后一抽达到的点数是否超过K
,所以我们能够达到的最小点数为K
,最大点数为K-1+W
,也就是说最后一抽的前一抽是已经达到能满足继续抽的最大条件,再抽一次必然达到停止抽牌的条件,但是达到最大值,不一定胜利,也就是超过了N
值。我们用dp[x]
表示已得x
分时再抽一次的胜利概率。
从例3来看,N=21,K=17,W=10
,最大能抽到得分数为17-1+10=26
,其中确定胜利得分数有大于等于17且小于21时,停止抽牌,此时胜利得概率为1
,而超过21
时,胜利概率为0
,也就是
dp[17]=dp[18]=dp[19]=dp[20]=dp[21] = 1;
dp[22]=dp[23]=dp[24]=dp[25]=dp[26] = 0;
当x=16
时,再抽一次可以是[1-10],概率相同都是1/W,胜利的概率就是能达到的点数的胜利概率之和,也就是
dp[16] = dp[17]*1/W+dp[18]*1/W+dp[19]*1/W+dp[20]*1/W
+dp[21]*1/W+dp[22]*1/W+dp[23]*1/W+dp[24]*1/W+dp[25]*1/W+dp[26]*1/W
所以
dp[x] = (dp[x+1]+dp[x+2]+...+dp[x+w])/W
此时 0=<x<K;
dp[x+1] = (dp[x+2]+..dp[x+W+1])/W
dp[x]-dp[x+1] = (dp[x+1]-dp[x+W+1])/W
dp[x]=dp[x+1]-(dp[x+W+1]-dp[x+1])/W
此时 0<=x<K-1
但是,这样K-1
就需要单独考虑
dp[K−1]=(dp[K]+dp[K+1]+⋯+dp[K+W−1])/W
注意到只有当K≤x≤min(N,K+W−1) 时才有 dp[x]=1,因此
dp[K−1]=(min(N,K+W−1)−K+1)/W=(min(N−K+1,W))/W
代码:
class Solution {
public:
double new21Game(int N, int K, int W) {
if (K == 0) {
return 1.0;
}
vector<double> dp(K + W + 1);
for (int i = K; i <= N && i < K + W; i++) {
dp[i] = 1.0;
}
dp[K - 1] = 1.0 * 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];
}
};