链接:https://vjudge.net/contest/400607#problem/D
突然想起来概率里面还有一个概率dp,这个之前确实学的不扎实,通过这道题回顾一下。
题意:打一局游戏的胜率是p,初始开箱爆率是q=2%,每打一局游戏如果赢了,可以开箱,如果没开中,开箱爆率会加2%(最大为100%)。如果游戏输了,开箱爆率为加1.5%,求开中箱需要玩多少局游戏的期望。
解法:
这是一道期望dp,期望dp一般来讲是逆推
设dp[p][q]为胜率为p,开箱率为q的局数的期望。
注:因为dp的下标都是整数,会出现1.5这种的话,我们让q为1000
考虑一下状态转移方程:
dp[p][q] = 1.0 + dp[p][q+15](1-p) + dp[p][q+20]*p(1-q)
理解为这一局+后面的期望局数
最终状态dp[p][1000]表示胜率为p,开箱率为100%的局数的期望,也就是一个伯努利实验,那么伯努利实验的结论是期望=概率的倒数,也就是说dp[p][1000]=1.0/p
实现方式,记忆化搜索更方便一些。
代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1010;
double dp[110][maxn];

double dfs(int p, int q) {
    if(q >= 1000) return 100.0/p;
    if(dp[p][q] != -1) return dp[p][q];
    dp[p][q] = 1.0 + dfs(p, q + 15) * (1.0 - p/100.0) + dfs(p, q + 20) * (p/100.0) * (1.0 - q/1000.0);
    return dp[p][q];
}

int main()
{
    int t, p, cnt = 0;
    scanf("%d",&t);
    for(int i = 0; i < 110; i++) {
        for(int j = 0; j < maxn; j++) {
            dp[i][j] = -1;
        }
    }
    while(t--)
    {
        scanf("%d",&p);
        printf("Case %d: %.6lf\n", ++cnt ,dfs(p, 20));
    }
    return 0;
}