跳转至

老虎机

https://codeforces.com/contest/2181/problem/J

题意

\(A\)\(1000\)美元,去赌场玩老虎机,老虎机的中奖概率是由一个序列决定的

在制作这个老虎机时,随机从概率序列中选择一个\(a_i\),并让这个老虎机中奖的概率为\(a_i\%\)

注意:老虎机只会有一个固定的中奖概率,且\(A\)不知道,\(A\)只知道这个序列

一共有\(k\)次游玩机会,每次游玩可以选择投入\(X\),且不能贷款,\(X\)可以等于\(0\)

这个老虎机只会有两种结果

  • 中奖,收回\(2X\)
  • 未中奖,收回\(0\)

给定\(n,k\)以及

一个长度为\(n\)的序列,表示\(n\)个概率,分别为\(a_1,a_2,a_3,...,a_n\),每个数表示的是中奖概率为\(a_i\%\)

\(k\)表示\(A\)最多可以玩的局数

思路

考虑一段固定长度为\(a+b\)的前缀序列,包含\(a\)次中奖、\(b\)次未中奖,对应概率为\(p^a(1-p)^b\)

因此可以等效看作存在一个自动机,按如下概率生成下一个结果为头奖:

\[p(a,b) = \frac{\sum p^{a+1}(1-p)^b}{\sum p^a(1-p)^b}\]

其中\(a\)为当前已中奖次数,\(b\)为当前未中奖次数

接下来定义状态\(dp(a,b,x)\):表示当前已中奖\(a\)次、未中奖\(b\)次、持有资金\(x\)时,能获得的最大期望收益

状态转移方程为:

\(a+b < k\)时:

\[dp(a,b,x)=\max_{\substack{0\leq y\leq x}}\big(p(a,b)\cdot dp(a+1,b,x+y)+(1-p(a,b))\cdot dp(a,b+1,x-y)\big)\]

\(a+b = k\)时:

\[dp(a,b,x) = x\]

易得最优策略仅为全额下注或不下注

于是可设\(dp(a,b,x)=C[a][b]\cdot x\),递推公式改写为:

\(a+b < k\)时:

\[C[a][b] = \max\big(p(a,b)C[a+1][b]+(1-p(a,b))C[a][b+1],\ 2p(a,b)C[a+1][b]\big)\]

\(a+b = k\)时:

\[C[a][b]=1\]

代码

#include<bits/stdc++.h>
#define Maxn 30
#define Maxm 1000000
using namespace std;
int n,k;
double a[Maxm+5];
double dp[Maxn+5][Maxn+5],f[Maxn+5][Maxn+5];
double qp(double x,int y)
{
    double ans=1;
    while(y)
    {
        if(y&1) ans*=x;
        y>>=1;
        x*=x;
    }
    return ans;
}
double p(int x,int y)
{
    if(f[x][y]) return f[x][y];
    double sum1=0,sum2=0;
    for(int i=1;i<=n;i++)
    {
        sum1+=qp(a[i]/100,x+1)*qp(1-a[i]/100,y);
        sum2+=qp(a[i]/100,x)*qp(1-a[i]/100,y);
    }
    if(sum2==0.0)
    {
        if(sum1==0.0) return f[x][y]=0;
        return f[x][y]=1;
    }
    return f[x][y]=sum1/sum2;
}
double dfs(int x,int y)
{
    if(dp[x][y]) return dp[x][y];
    if(x+y>=k) return dp[x][y]=1;
    return dp[x][y]=max(p(x,y)*dfs(x+1,y)+(1-p(x,y))*dfs(x,y+1),2*p(x,y)*dfs(x+1,y));
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%lf",a+i);
    printf("%.5f\n",1000*(dfs(0,0)-1));
    return 0;
}