Open azl397985856 opened 3 years ago
class Solution {
public double new21Game(int N, int K, int W) {
double[] DP=new double[K+W];
if(K==0)
return 1;
DP[0]=1;
DP[1]=1d/W;
for(int a=2;a<=K+W-1;a++)
DP[a]=(DP[a-1]*(W+(a>K?0:1))-(a-W-1>=0?DP[a-W-1]:0))/W;
return Arrays.stream(DP).limit(N+1).skip(K).sum();
}
}
复杂度分析
令 n 为数组长度。
需要用动态规划来做, 然后可以用滑动窗口来优化。
假如 w=4, 那么每一轮随机得到的值的区间是[1, W], 即 [1,2,3,4],
dp数组: dp[i]: 中间路过分数 i 的概率. 看作一条路径, 在这条路径上能不能到达某个分数 i。
dp[i]之前的与之有关的状态有:
dp[i-4]
dp[i-3]
dp[i-2]
dp[i-1]
dp[i-1] -> dp[i-W] 范围内的有效状态, 满足:
i - W >= 0
且 i - W < k
w=4时, 如何计算dp[8]?
反推:
8 = 1+7=2+6=3+5=4+4
故 dp[8] = 1/4 dp[1] + 1/4 dp[2] + 1/4 dp[3] + 1/4 dp[4]
实现语言: C++
class Solution {
public:
double new21Game(int N, int k, int W) {
double dp[20005] = {0};
double p = 1.0 / W;
double sum = 0;
dp[0] = 1;
int l = 0, r = -1;
for (int i = 0; i <= k - 1 + W; i++)
{
dp[i] += p * sum;
if (i < k)
sum += dp[i];
r = i;
while (r - l + 1 > W)
sum -= dp[l++];
}
double res = 0;
for (int i = k; i <= N; i++)
res += dp[i];
return res;
}
};
动态规划。
class Solution {
public:
double new21Game(int n, int k, int maxPts) {
if (k == 0) return 1.0;
vector<double> dp(k + maxPts);
for (int i = 0; i <= n && i < k + maxPts; i++) dp[i] = 1.0;
dp[k - 1] = 1.0 * min(n - k + 1, maxPts) / maxPts;
for (int i = k - 2; i >= 0; i--) dp[i] = dp[i + 1] - (dp[i + maxPts + 1] - dp[i + 1]) / maxPts;
return dp[0];
}
};
var new21Game = function (n, k, maxPts) {
const dp = new Array(k + maxPts + 2).fill(0);
let windowSum = 0;
for (let i = k; i < k + maxPts; i++) {
if (i <= n) dp[i] = 1;
windowSum += dp[i];
}
for (let i = k - 1; i >= 0; i--) {
dp[i] = windowSum / maxPts;
windowSum -= dp[i + maxPts];
windowSum += dp[i];
}
return dp[0];
};
# dp[i] = (dp[i+1] + dp[i+2] + ... + dp[i+W]) / W
class Solution: def new21Game(self, n: int, k: int, maxPts: int) -> float: dp = [0] * (k + maxPts) win_sum = 0 for i in range(k, k + maxPts): if i <= n: dp[i] = 1 win_sum += dp[i]
for i in range(k - 1, -1, -1):
dp[i] = win_sum / maxPts
win_sum += dp[i] - dp[i + maxPts]
return dp[0]
## 复杂度
- 时间: O(k + w)
- 空间: O(k + w)
动态规划: 最终落在k到n直接为赢,胜利概率为1,大于n为输,概率为0。因为抽卡概率均等,所以k-1时抽卡胜利的概率以k-1为起点,maxPts为步长的概率和除以maxPts,向前递推直至0起点
func new21Game(n int, k int, maxPts int) float64 {
dp := make([]float64,k + maxPts)
sum:=0.0
for i := k;i<k+maxPts;i++ {
if i<=n {
dp[i] = 1.0
sum+=dp[i]
}else {
break
}
}
for i := k-1;i>=0;i-- {
dp[i] = sum/float64(maxPts)
sum = sum+dp[i] - dp[i+maxPts]
}
return dp[0]
}
https://leetcode-cn.com/problems/new-21-game/
DP, 状态转移公式dp[i] = 1/w * (dp[i - 1] + ... + dp[i - w])
class Solution {
public double new21Game(int n, int k, int w) {
if(n >= k + w - 1) return 1.0;
if(n < k) return 0.0;
if(k == 0) return 1.0;
//dp[i] = 1/w * (dp[i - 1] + ... + dp[i - w])
double[] dp = new double[k + w];
double prob = (double)(1) / (double)(w);
dp[0] = 1.0;
double sum = 1.0;
for(int i = 1; i < k + w; i++) {
dp[i] = prob * sum;
if(i < k) sum += dp[i];
if(i - w >= 0) sum -= dp[i - w];
}
double res = 0.0;
for(int i = k; i <= n; i++) {
res += dp[i];
}
return res;
}
}
O(k + w)
O(k + w)
dp + sliding window.
class Solution:
def new21Game(self, n: int, k: int, maxPts: int) -> float:
dp = [0]*(k + maxPts)
win = 0
for i in range(k,k+maxPts):
if i <= n:
dp[i] = 1
win += dp[i]
for i in range(k-1,-1,-1):
dp[i] = win/maxPts
win = win - dp[i+maxPts] + dp[i]
return dp[0]
Nothing
p0: 1
p1: p0 * 1/w
p2: p1 * 1/w + p0 * 1/w
p3: p2 * 1/w + p1 * 1/w + p0 * 1/w
能走到:从K开始直到N,这些位置的概率的和
AC
class Solution {
public double new21Game(int N, int K, int W) {
if (K == 0 || N >= K + W) return 1;
double dp[] = new double[N + 1], Wsum = 1, res = 0;
dp[0] = 1;
for (int i = 1; i <= N; ++i) {
dp[i] = Wsum / W;
if (i < K) Wsum += dp[i]; else res += dp[i];
if (i - W >= 0) Wsum -= dp[i - W];
}
return res;
}
}
time: O(N) space: O(N)
class Solution {
public double new21Game(int n, int k, int maxPts) {
if (n >= k + maxPts - 1) return 1.0;
double prob = 1.0 / maxPts, prev = 0;
double[] prbArray = new double[k + maxPts];
prbArray[0] = 1;
for (int i = 1; i <= k; i++){
prev = prev - (i - maxPts - 1 >= 0 ? prbArray[i - maxPts - 1] : 0) + prbArray[i - 1];
prbArray[i] = prev * prob;
}
double res = prbArray[k];
for (int i = k + 1; i <= n; i++){
prev = prev - (i - maxPts - 1 >= 0 ? prbArray[i - maxPts - 1] : 0);
prbArray[i] = prev * prob;
res += prbArray[i];
}
return res;
}
}
Time Complexity: O(n), Space Complexity: O(n)
DP + sliding window, 自己对DP没有思路,需要加强
class Solution:
def new21Game(self, N: int, K: int, W: int) -> float:
dp = [0] * (K + W)
win_sum = 0
for i in range(K, K + W):
if i <= N:
dp[i] = 1
win_sum += dp[i]
for i in range(K - 1, -1, -1):
dp[i] = win_sum / W
win_sum += dp[i] - dp[i + W]
return dp[0]
public double new21Game(int n, int k, int maxPts) {
return dfs(n, k, maxPts, 0);
}
private double dfs(int n, int k, int maxPts, int currentTotal) {
if (currentTotal >= k) {
if (currentTotal <= n) {
return 1D;
}
return 0;
}
double probability = 0;
for (int i = 1; i <= maxPts; i++) {
probability += dfs(n, k, maxPts, currentTotal + i) * (1D / maxPts);
}
return probability ;
}
//dp[i] = (dp[i-1] + dp[i-2] + ... + dp[i-maxPts]) * 1/maxPts
public double new21Game(int n, int k, int maxPts) {
double[] dp = new double[n + 1];
dp[0] = 1D;
for (int i = 1; i <= n; i++) {
for (int j = i - maxPts; j <= i - 1; j++) {
if (j < 0) {
continue;
}
if (j >= k) {
continue;
}
dp[i] += dp[j] * (1D / maxPts);
System.out.println("i = " + i + " dp: " + dp[i]);
}
}
double res = 0;
for (int i = k; i <= n; i++) {
res += dp[i];
}
return res;
}
public double new21Game(int n, int k, int maxPts) {
if (k == 0 || n >= k + maxPts - 1) {
return 1.0;
}
double[] dp = new double[k + maxPts];
dp[0] = 1D;
double sum = 1D;
for (int i = 1; i < k + maxPts; i++) {
dp[i] = sum * 1D / maxPts;
if (i < k) {
sum += dp[i];
}
if (i - maxPts >= 0) {
sum -= dp[i - maxPts];
}
}
double res = 0;
for (int i = k; i <= n; i++) {
res += dp[i];
}
return res;
}
class Solution:
def new21Game(self, n: int, k: int, maxPts: int) -> float:
if k == 0 or n >= k + maxPts:
return 1
probs = [0] * (n+1)
probs[0] = 1
left, right = 0, 1
total = sum(probs[left:right])
for i in range(1, n+1):
start = max(0, i - maxPts)
end = min(k, i)
total += sum(probs[right:end])
total -= sum(probs[left:start])
probs[i] += total / maxPts
left, right = start, end
return sum(probs[k:n+1])
My initial thought: Irwin–Hall distribution LOL
Time: O(nk)
Space: O(n)
DP, sliding window. The possible points is [0, N]. Let dp[i] be the probablity to get i points. When we draw w from [1, W], the probablity to get i points after draw w is dp[i-w] / W. We can maintain a sliding window with size W. The sliding window's sum is the probability to get i-1 ~ i-W points. Everytime when we meet i - W >= 0 we need to increase the window's left side by 1. In other words we need to decrease the dp[i-W].
class Solution:
def new21Game(self, n: int, k: int, W: int) -> float:
if k <= 0 or n >= k+W-1: return 1
dp = [0] * (n+1)
dp[0] = 1
Wsum = 1
for i in range(1, n+1):
dp[i] = Wsum/W
if i < k: Wsum += dp[i]
if i - W >= 0: Wsum -= dp[i-W]
return sum(dp[k:])
Time: O(N) Space; O(N)
dp,但朴素的dp超时,需要优化,于是再原有状态方程基础上优化差分,得到o(1)的计算时间
def new21Game(self, n: int, k: int, maxPts: int) -> float:
if k == 0:
return 1.0
dp = [0.0] * (k + maxPts)
for i in range(k, min(n, k + maxPts - 1) + 1):
dp[i] = 1.0
dp[k - 1] = float(min(n - k + 1, maxPts)) / maxPts
for i in range(k - 2, -1, -1):
dp[i] = dp[i + 1] - (dp[i + maxPts + 1] - dp[i + 1]) / maxPts
return dp[0]
# dp + sliding window
class Solution:
def new21Game(self, n: int, k: int, maxPts: int) -> float:
if k == 0 or n >= k + maxPts:
return 1
dp = [0 for i in range(n + 1)]
win = 1
res = 0
dp[0] = 1
for i in range(1, n + 1):
dp[i] = win / maxPts
if i < k:
win += dp[i]
else:
res += dp[i]
if i - maxPts >= 0:
win -= dp[i - maxPts]
return res
https://leetcode.com/problems/new-21-game/
const new21Game = function (n, k, maxPts) {
if (k + maxPts <= n || k === 0) return 1;
let dp = [];
dp[0] = 1;
dp[1] = 1 / maxPts;
for (let i = 2; i <= n; i++) {
dp[i] = 0;
if (i <= k) {
dp[i] = (1 + 1 / maxPts) * dp[i - 1];
} else {
dp[i] = dp[i - 1];
}
if (i > maxPts) {
dp[i] -= dp[i - maxPts - 1] / maxPts;
}
}
return dp.reduce((acc, cur, idx) => {
if (idx >= k) {
acc += cur;
}
return acc;
}, 0);
};
上期做完之后还是不太会。看了leetcode上有个题解讲的非常明白 Alice的得分情况,最多k + maxPts - 1,最少0分。其中当得分 >= k时,因为不能抽牌了,所以如果得分<=n胜率为1,>n胜率为0。 之后从得分k-1开始填概率,因为此时可以抽到[1...maxPts]任意一张牌,概率为右侧maxPts个格子的概率和再除以maxPats 依次推到得分0的概率就是答案。 https://leetcode-cn.com/problems/new-21-game/solution/huan-you-bi-zhe-geng-jian-dan-de-ti-jie-ma-tian-ge/
class Solution {
public double new21Game(int n, int k, int maxPts) {
if (k == 0) {
return 1.0;
}
double[] dp = new double[k + maxPts];
double sum = 0.0;
for (int i = k; i <= n && i < k + maxPts; i++) {
dp[i] = 1.0;
sum += dp[i];
}
for (int i = k - 1; i >= 0; i--) {
dp[i] = sum / maxPts;
sum += dp[i] - dp[i+maxPts];
}
return dp[0];
}
}
TC: O(k + maxPts) SC: O(k + maxPts)
Sliding window + DP 没看懂题 看的答案思路
class Solution:
def new21Game(self, n: int, k: int, maxPts: int) -> float:
#dp[i] = sum(dp[i+j] for j in range(1,maxPts+1))/W
dp = [0] * (k + maxPts)
curr_sum = 0
for i in range(k, k+maxPts):
if i <= n:
dp[i] = 1
curr_sum += 1
for i in range(k-1, -1, -1):
dp[i] = curr_sum / maxPts
curr_sum = curr_sum - dp[i+maxPts] + dp[i]
return dp[0]
Space: O(k+maxPts) Time: O(k+maxPts)
思路: 对动态规划不是很熟,感觉是列状态方程再分析。我在研究一下
func new21Game(n int, k int, maxPts int) float64 {
if k == 0{
return 1.0
}
win_sum := 0.0
dp := make([]float64, k+maxPts)
for i:=k; i <= n&& i < k+maxPts; i++{
dp[i] = 1.0
win_sum += dp[i]
}
for i := k-1; i >= 0 ; i--{
dp[i] = win_sum/float64(maxPts)
win_sum += dp[i] - dp[i+maxPts]
}
return dp[0]
}
时间复杂度:O(K+W)O(K+W) 空间复杂度:O(K+W)O(K+W)
class Solution {
public double new21Game(int N, int K, int W) {
if (N - K + 1 >= W) {
return 1.0;
}
double[] dp = new double[K + W];
for (int i = K; i <= N; i++) {
dp[i] = 1.0;
}
double sumProb = N - K + 1;
for (int i = K - 1; i >= 0; i--) {
dp[i] = sumProb / W;
sumProb = sumProb - dp[i + W] + dp[i];
}
return dp[0];
}
}
时间复杂度:O(N) 空间复杂度:O(K+W)
class Solution:
def new21Game(self, n: int, k: int, maxPts: int) -> float:
dp = [0] * (k + maxPts)
win_sum = 0
for i in range(k, k + maxPts):
if i <= n:
dp[i] = 1
win_sum += dp[i]
for i in range(k - 1, -1, -1):
dp[i] = win_sum / maxPts
win_sum += dp[i] - dp[i + maxPts]
return dp[0]
Time and Space: O(K+W)
if k == 0:
return 1
dp = [0]*(n+1)
dp[0] = 1
winSum = 1
for i in range(1, n+1):
dp[i] = winSum/float(maxPts)
if i<k:
winSum += dp[i]
if i-maxPts>=0 and i-maxPts<k:
winSum -= dp[i-maxPts]
return sum(dp[k:])
在k+maxPts之前不可能出现溢出。那么就需要考虑k+maxPts之内的问题。如果最后一次在[k,n]之间,肯定不会溢出,设定为1。在k之前的每一次,转移到h后面maxPts个的概率是和除以maxPts。
class Solution:
def new21Game(self, n: int, k: int, maxPts: int) -> float:
dp = [0] * (k+maxPts)
win_sum = 0
for i in range(k,k + maxPts):
if i<=n:
dp[i] = 1
win_sum += dp[i]
for i in range(k-1,-1,-1):
dp[i] = win_sum/maxPts
win_sum += dp[i] - dp[i+maxPts]
return dp[0]
时间复杂度 :O(k+maxPts)
空间复杂度:O(k+maxPts)
求每种可能的不超过 N 的概率,超过 K 不能再改变,因此超过 K 的情况是否超过 N 是确定的,通过从后往前转移计算初始值为 0时 不超过 N 的概率, 结合滑动窗口优化区间求值
class Solution {
public double new21Game(int n, int k, int maxPts) {
double[] dp = new double[k + maxPts];
double window = 0;
for (int i = k; i < k + maxPts; ++i) {
if (i <= n) {
dp[i] = 1;
} else {
dp[i] = 0;
}
window += dp[i];
}
for (int i = k - 1; i >= 0; --i) {
dp[i] = window / maxPts;
window = window - dp[i + maxPts] + dp[i];
}
return dp[0];
}
}
837. 新 21 点
入选理由
暂无
题目地址
https://leetcode-cn.com/problems/new-21-game
前置知识
暂无
题目描述
爱丽丝参与一个大致基于纸牌游戏 “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 提示: 0 <= K <= N <= 10000 1 <= W <= 10000 如果答案与正确答案的误差不超过 10^-5,则该答案将被视为正确答案通过。 此问题的判断限制时间已经减少。
动态规划略显还是不够,保留滑动窗口结果不重复计算才是王道
class Solution: def new21Game(self, n: int, k: int, maxPts: int) -> float: #动态规划来做 #确定状态定义 #dp[i]表示当前分数为i时,分数不超过N的概率 #dp[i]=sum(dp[i+1]+......+dp[i+w])*1/w #初始值,dp[i+1]......dp[i+w],分数小于N直接给1,因为大于k已经停止抽取 dp=[0]*(k+maxPts) sum_value1=0 for i in range(k,k+maxPts):#当前已经小于等于N了,所以直接概率就是1 if i<=n: dp[i]=1 sum_value1+=dp[i] #滑动窗口才可以通过
for i in range(k-1,-1,-1):
if i==k-1:
dp[i]=sum_value1/maxPts
else:
sum_value1=sum_value1+dp[i+1]-dp[i+maxPts+1]
dp[i]=sum_value1/maxPts
return dp[0]
时间复杂度:O(k + w)
空间复杂度:O(k + w)
class Solution { public double new21Game(int n, int k, int maxPts) { if (k == 0) { return 1.0; } double[] dp = new double[k + maxPts]; double sum = 0.0; for (int i = k; i <= n && i < k + maxPts; i++) { dp[i] = 1.0; sum += dp[i]; }
for (int i = k - 1; i >= 0; i--) {
dp[i] = sum / maxPts;
sum += dp[i] - dp[i+maxPts];
}
return dp[0];
}
}
class Solution {
public double new21Game(int N, int K, int W) {
double[] dp = new double[K + W];
double sum = 0;
for (int i = K; i < K + W; i++) {
if (i <= N) {
dp[i] = 1;
} else {
dp[i] = 0;
}
sum += dp[i];
}
for (int i = K - 1; i >= 0; i--) {
dp[i] = sum / W;
sum = sum + dp[i] - dp[i + W];
}
return dp[0];
}
}
思路:
class Solution {
public double new21Game(int n, int k, int maxPts) {
// corner cass
if (n >= k + maxPts || k == 0) return 1;
double[] dp = new double[k+maxPts];
dp[0] = 1;
for (int i = 1; i < k + maxPts; i++) {
for (int pt = 1; pt <= maxPts; pt++) {
if (i - pt >= 0 && i - pt < k) {
dp[i] += dp[i-pt] / maxPts;
}
}
}
double result = 0;
for (int i = k; i <= n; i++) {
result += dp[i];
}
return result;
}
}
Time complexity: O((k+maxPts) * maxPts) Space complexity: O(k+maxPts)
思路:
class Solution {
public double new21Game(int n, int k, int maxPts) {
// corner cases
if (n >= k + maxPts || k == 0) return 1;
double[] dp = new double[n+1];
dp[0] = 1;
double pSum = 1, result = 0;
for (int i = 1; i <= n; i++) {
dp[i] = pSum / maxPts;
if (i < k) {
pSum += dp[i];
} else {
result += dp[i];
}
if (i - maxPts >= 0) {
pSum -= dp[i - maxPts];
}
}
return result;
}
}
Time complexity: O(n) Space complexity: O(n)
参考的leetcode 官方题解 用动态规划表示在i分的赢的概率
从k 到 min(n, k+maxPts) 因为都会赢, 所以dp[i] = 1.0
从后往前推
dp[k-1] = (min(n, k + maxPts - 1) - k + 1) / maxPts 也就是从k到 min(n, k+maxPts) 之间的概率的和
剩下的0到k-2
dp[i] = sum(dp[i+1:i+maxPts+1])/maxPts
可以优化为,其实就是sliding window dp[i] = dp[i+1] - (dp[i+1+maxPts]-dp[i+1])/maxPts
class Solution:
def new21Game(self, n: int, k: int, maxPts: int) -> float:
if k == 0:
return 1.0
dp = [0.0] * (k + maxPts + 1)
for i in range(k, min(n, k + maxPts) + 1):
dp[i] = 1.0
dp[k - 1] = (min(n, k + maxPts - 1) - k + 1) / maxPts
for i in range(k - 2, -1, -1):
dp[i] = dp[i + 1] - (dp[i + 1 + maxPts] - dp[i + 1]) / maxPts
return dp[0]
Time O(k+maxPts)
Space O(k+maxPts)
滑动窗口+动态规划
class Solution {
public:
double new21Game(int n, int k, int maxPts) {
vector<double>dp(k+maxPts,0.0);
double s=0;
for(int i=k;i<k+maxPts;i++)
{
dp[i]= i<=n?1:0;
s+=dp[i];
}
for(int i=k-1;i>=0;--i)
{
dp[i]=s/maxPts;
s=s-dp[i+maxPts]+dp[i];
}
return dp[0];
}
};
复杂度分析
思路: 这道题问的是 最终得分sum 落在 k<= sum <= n 这个区间的概率是多少。创建动态规划array dp,让dp[i] 是在i这个数时赢的概率(赢就是说最终得分小于等于n)。 先考虑base cases: 如果累积的数是大于等于k的话就已经没有机会再抽牌了,所以概率是固定的。 所以如果 k <= i <= n的话 赢的概率一定为1。 对于 k<= i <= n的i, dp[i] = 1. 再考虑i是小于k的情况,这个时候还可以继续抽牌,有1/maxPts的几率抽到1 到maxPts中任何数。所以再抽一次的话, 总数将是在 i + 1, i + 2,......,i + maxPts中某一个, 所以在i这个数时赢的可能性是 在i+1, i+2,...,i+maxPts 赢的可能性的总和 除以 maxPts (除以maxPts因为有 1/maxPts 几率抽到1 到maxPts中任何数) 所以 dp[i] = sum(dp[i + j] for j in range(1, maxPts)) / maxPts。 我们反向dp,从 k -1 开始 动态转移,因为直接动态规划的runtime是O(K* maxPts) 会TLE, 我们维持一个sliding window sum, 因为从计算dp[i]到计算dp[i-1] 我们可以对sliding window sum 做出小修改:加入dp[i] 和减去dp[i+maxPts]. Python 3 code
class Solution:
def new21Game(self, n: int, k: int, maxPts: int) -> float:
dp = [0] * (k + maxPts)
window_sum = 0
for i in range(k, k + maxPts):
if i <= n:
dp[i] = 1
window_sum += dp[i]
for i in range(k - 1, -1, -1):
dp[i] = window_sum / maxPts
window_sum += dp[i] - dp[i + maxPts]
return dp[0]
Time Complexity: O(k + maxPts) Space Complexity: O(k + maxPts)
class Solution: def new21Game(self, n: int, k: int, maxPts: int) -> float: dp = [0] * (k + maxPts) win_sum = 0 for i in range(k, k + maxPts): if i <= n: dp[i] = 1 win_sum += dp[i]
for i in range(k - 1, -1, -1):
dp[i] = win_sum / maxPts
win_sum += dp[i] - dp[i + maxPts]
return dp[0]
class Solution:
def new21Game(self, n: int, k: int, maxPts: int) -> float:
#n = 21 # prob point
#k = 17 # draw less than
#maxPts = 10 # card
'''
dp = [0 for i in range(k)]
dp += [1 for i in range(k, n+1,1)]
dp += [0 for i in range(n+1, n*2,1)]
for i in range(k-1 ,-1, -1):
dp[i] = sum(dp[i+1: i+1+maxPts]) /maxPts
return dp[0]
'''
dp = [0.0] * (2*n + maxPts )
for i in range(k,n+1):
dp[i] = 1.0
prob = (n-k+1)
for i in range(k -1, -1,-1):
dp[i] = prob/maxPts
prob += (dp[i] - dp[i+maxPts] )
return dp[0]
class Solution:
def new21Game(self, n: int, k: int, maxPts: int) -> float:
if k == 0 or n >= k + maxPts:
return 1
dp = [1.0] + [0.0] * n
Wsum = 1.0
for i in range(1, n + 1):
dp[i] = Wsum / maxPts
if i < k: Wsum += dp[i]
if i - maxPts >= 0: Wsum -= dp[i - maxPts]
return sum(dp[k:])
class Solution:
def new21Game(self, n: int, k: int, maxPts: int) -> float:
if k == 0:
return 1.0
dp = [0.0] * (k + maxPts)
for i in range(k, min(n, k + maxPts - 1) + 1):
dp[i] = 1.0
dp[k - 1] = float(min(n - k + 1, maxPts)) / maxPts
for i in range(k - 2, -1, -1):
dp[i] = dp[i + 1] - (dp[i + maxPts + 1] - dp[i + 1]) / maxPts
return dp[0]
思路
dp+滑动窗口
代码
class Solution {
public double new21Game(int N, int K, int W) {
if (K == 0 || N >= K + W) return 1;
double dp[] = new double[N + 1], Wsum = 1, res = 0;
dp[0] = 1;
for (int i = 1; i <= N; ++i) {
dp[i] = Wsum / W;
if (i < K) Wsum += dp[i]; else res += dp[i];
if (i - W >= 0) Wsum -= dp[i - W];
}
return res;
}
}
复杂度
时间复杂度:O(K+W)
空间复杂度:O(K+W)
没什么思路
居然是转移方程式:分数大于等于 K 的这些情况中不大于 N 的概率
dp[i] = sum(dp[i+j] for j in range(1,W+1)/W
def nuw21Game(self, N:int, K:int, W:int):
dp=[0]*(K+W)
for i in range(K,K+W):
if i<=N:
dp[i] = 1
for i in range(k-1,-1,-1):
dp[i] = sum(dp[i+j] for j in range(1,w+1))/W
Return dp[0]
Dp 就是linear time和sapce 啦!
class Solution:
def new21Game(self, N: int, K: int, W: int) -> float:
dp = [0] * (K + W)
win_sum = 0
for i in range(K, K + W):
if i <= N:
dp[i] = 1
win_sum += dp[i]
for i in range(K - 1, -1, -1):
dp[i] = win_sum / W
win_sum += dp[i] - dp[i + W]
return dp[0]
dp[i]
代表有 i 分时赢得概率
base case
当 $i \in [k, min(n, k+maxPts-1]$ 时, dp[i] = 1 (题目定义)
当 $i \in [min(n, k+maxPts-1, )$ 时, dp[i] = 0 (题目定义)
动态转移
因为每次可能得到的数是 [1, maxPts] 而且每个数是相同 probability 的, 所以 dp[i] 的 概率就是 后面 maxPts 个数的赢得概率 除以 maxPts
dp[i] = sum(dp[i+1:i+maxPts]) / maxPts
可以用滑动窗口优化上面的 sum 的求解, 每次加入一个新的数组并移除末端的数字
return
dp[0] 代表 有 0 分时 赢得概率
class Solution:
def new21Game(self, n: int, k: int, maxPts: int) -> float:
# dp[i] is the probability of winning when Alice have i points
dp = [0] * (k+maxPts)
w = 0
for i in range(k+maxPts-1, -1, -1):
if i >= k:
# if i >= k, then the probability depends on if i <= n based on question
dp[i] = 1 if i <= n else 0
w += dp[i]
else:
# if i < k, then the number outcome [1, maxPts] have equal probability
# so p[i] = sum(p[i+1:i+maxPts]) / (maxPts)
# use sliding window approach to calculate
dp[i] = w / maxPts
w = w + dp[i] - dp[i+maxPts]
return dp[0]
时间复杂度: O(k+maxPts) dp 数组的遍历复杂度, 每次复杂度是 O(1)
空间复杂度: O(k+maxPts) dp 数组的空间复杂度
从后往前动态规划,以及滑块
class Solution {
public double new21Game(int n, int k, int maxPts) {
double[] dp = new double[k + maxPts];
double sum = 0;
for (int i = k; i < dp.length; i++) {
dp[i] = i > n ? 0 : 1;
sum += dp[i];
}
for (int i = k - 1; i >= 0; i--) {
dp[i] = sum / maxPts;
sum = sum - dp[i + maxPts] + dp[i];
}
return dp[0];
}
}
class Solution: def new21Game(self, n: int, k: int, maxPts: int) -> float: W= maxPts K=k N=n dp = [0] * (K + W) win_sum = 0 for i in range(K, K + W): if i <= N: dp[i] = 1 win_sum += dp[i]
for i in range(K - 1, -1, -1):
dp[i] = win_sum / W
win_sum += dp[i] - dp[i + W]
return dp[0]
https://leetcode.com/problems/new-21-game/
dp + slide window
class Solution:
def new21Game(self, n: int, k: int, maxPts: int) -> float:
if k == 0:
return 1.0
dp = [1.0] + [0.0] * n
t_sum = 1.0
for i in range(1, n+1):
dp[i] = t_sum / maxPts
if i < k:
t_sum += dp[i]
if 0 <= i - maxPts < k:
t_sum -= dp[i - maxPts]
return sum(dp[k:])
时间复杂度: O(n) 空间复杂度:O(n)
class Solution:
def new21Game(self, n: int, k: int, maxPts: int) -> float:
dp = [0] * (k + maxPts)
win_sum = 0
for i in range(k, k + maxPts):
if i <= n:
dp[i] = 1
win_sum += dp[i]
for i in range(k - 1, -1, -1):
dp[i] = win_sum / maxPts
win_sum += dp[i] - dp[i + maxPts]
return dp[0]
DP + sliding window
class Solution:
def new21Game(self, n: int, k: int, maxPts: int) -> float:
dp = [0 for _ in range(k+maxPts)]
s = 0
for i in range(k, min(n, k + maxPts - 1) + 1):
if i <= n:
dp[i] = 1
s += dp[i]
for i in range(k - 1, -1, -1):
dp[i] = s / maxPts
s += dp[i] - dp[i + maxPts]
return dp[0]
Time complexity: O(n). Space complexity: O(n)
Problem Link
Ideas
Complexity: hash table and bucket
Code
class Solution:
def new21Game(self, n: int, k: int, maxPts: int) -> float:
if k == 0:
return 1.0
dp = [0.0] * (k + maxPts)
for i in range(k, min(n, k + maxPts - 1) + 1):
dp[i] = 1.0
dp[k - 1] = float(min(n - k + 1, maxPts)) / maxPts
for i in range(k - 2, -1, -1):
dp[i] = dp[i + 1] - (dp[i + maxPts + 1] - dp[i + 1]) / maxPts
return dp[0]
Explanation
Python
class Solution:
def new21Game(self, N: int, K: int, W: int) -> float:
dp = [0 for _ in range(K + W)]
curr_sum = 0
for i in range(K, K+W):
if i <= N:
dp[i] = 1
curr_sum += dp[i]
for i in range(K-1, -1, -1):
dp[i] = curr_sum / W
curr_sum += dp[i] - dp[i+W]
return dp[0]
Complexity:
O(K+W)
O(K+W)
不太会dp, 看了解答才大概会。。 继续研究
class Solution {
public double new21Game(int N, int K, int W) {
if (K == 0 || N >= K + W) {
return 1;
}
double dp[] = new double[N + 1], Wsum = 1, res = 0;
dp[0] = 1;
for (int i = 1; i <= N; ++i) {
dp[i] = Wsum / W;
if (i < K) {
Wsum += dp[i];
}
else {
res += dp[i];
}
if (i - W >= 0) Wsum -= dp[i - W];
}
return res;
}
}
C++ Code:
int maxPoint = max(k -1 + maxPts, n) ;
vector<double> dp(maxPoint+1);
dp[0] =1.0; // base case.
for(int i=1; i<=maxPoint; i++)
{
for(int j = i - maxPts; j<i; j++)
{
if(j>=0 && j < k)
dp[i] += (dp[j] * 1.0/maxPts);
}
}
double ret =0;
for(int i = k; i<=n; i++)
{
ret +=dp[i];
}
return ret;
if( k == 0 || n >= k + maxPts)
return 1;
int maxPoint = max(k -1 + maxPts, n) ;
vector<double> dp(maxPoint+1);
dp[0] =1.0; // base case.
double sum =1;
for(int i =1; i<=min(maxPts, maxPoint ); i++)
{
dp[i] = sum/maxPts;
if(i<k)
sum +=dp[i];
}
for(int i = maxPts+1; i<=maxPoint; i++)
{
sum -=dp[i - maxPts-1];
dp[i] = sum/maxPts;
if(i<k)
sum +=dp[i];
}
// for(int i=1; i<=maxPoint; i++)
// {
// for(int j = i - maxPts; j<i; j++)
// {
// if(j>=0 && j < k)
// dp[i] += (dp[j] * 1.0/maxPts);
// }
// }
double ret =0;
for(int i = k; i<=n; i++)
{
ret +=dp[i];
}
return ret;
class Solution {
public double new21Game(int n, int k, int maxPts) {
if (k == 0) {
return 1.0;
}
double[] dp = new double[k + maxPts + 1];
for (int i = k; i <= n && i < k + maxPts; i ++) {
dp[i] = 1.0;
}
dp[k - 1] = 1.0 * Math.min(maxPts, n - k + 1) / maxPts;
for (int i = k - 2; i >= 0; i --) {
dp[i] = dp[i + 1] - (dp[i + maxPts + 1] - dp[i + 1]) / maxPts;
}
return dp[0];
}
}
837. 新 21 点
入选理由
暂无
题目地址
https://leetcode-cn.com/problems/new-21-game
前置知识
暂无
题目描述