• 【bzoj2318】Spoj4060 game with probability Problem 概率dp


    题目描述

    Alice和Bob在玩一个游戏。有n个石子在这里,Alice和Bob轮流投掷硬币,如果正面朝上,则从n个石子中取出一个石子,否则不做任何事。取到最后一颗石子的人胜利。Alice在投掷硬币时有p的概率投掷出他想投的一面,同样,Bob有q的概率投掷出他相投的一面。

    现在Alice先手投掷硬币,假设他们都想赢得游戏,问你Alice胜利的概率为多少。

    输入

    第一行一个正整数t,表示数据组数。

    对于每组数据,一行三个数n,p,q。

    输出

    对于每组数据输出一行一个实数,表示Alice胜利的概率,保留6位小数。 

    样例输入

    1
    1 0.5 0.5

    样例输出

    0.666667

    提示

    数据范围:
    1<=t<=50
    0.5<=p,q<=0.99999999
    对于100%的数据 1<=n<=99999999
    题解

    概率dp

    这题真是巨坑。。。

    f[i]表示i块石头先投者获胜的概率,g[i]表示i块石头后投者获胜的概率。

    易推出:

    $f[i]=frac{p_0·g[i-1]+(1-p_0)·q_0·f[i-1]}{1-(1-p_0)·(1-q_0)}$

    $g[i]=frac{q_0·f[i-1]+(1-q_0)·p_0·g[i-1]}{1-(1-p_0)·(1-q_0)}$

    然而这里$p_0$和$q_0$都是目标概率,而题目中的p和q都是几率,

    所以需要根据情况决定是否想要正面朝上。

    根据方程的推导:

    A想让自己获胜的概率最大,即让$f[i]$最大。

    假设$g[i-1]-f[i-1]$不等于$0$,把$f[i]$的推导式展开,得:

    $f[i]=frac{p_0·g[i-1]+(1-p_0)·q_0·f[i-1]}{1-(1-p_0)·(1-q_0)}\ =frac{(p_0+q_0-p_0·q_0)·f[i-1]+p_0(g[i-1]-f[i-1])}{p_0+q_0-p_0·q_0}\ =f[i-1]+frac{p_0(g[i-1]-f[i-1])}{p_0+q_0-p_0·q_0}\ =f[i-1]+frac1{frac{p_0+q_0-p_0·q_0}{p_0(g[i-1]-f[i-1])}}\ =f[i-1]+frac1{frac{1-q_0+frac{q_0}{p_0}}{g[i-1]-f[i-1]}}$

    显然当$g[i-1]-f[i-1]>0$时,$p_0$越大越好;当$g[i-1]-f[i-1]<0$时,$p_0$越小越好。

    $q_0$的推导同理。

    于是可以得到结论:

    当f[i-1]<g[i-1]时,都想要正面朝上,$p_0=p$,$q_0=q$;

    当f[i-1]>g[i-1]时,都不想要正面朝上,$p_0=1-p$,$q_0=1-q$。

    但是n太大肿么办?

    于是用到概率黑科技:

    当n越来越大时,f[n]逐渐趋近于一个定值,而且题目中只要求保留6位小数。

    所以就此题而言f[1000+k]可以近似等于f[1000]。

    于是时间复杂度就降为O(1000T),可解。
    #include <cstdio>
    #include <cstring>
    double f[1001] , g[1001];
    int main()
    {
        int t;
        scanf("%d" , &t);
        while(t -- )
        {
            int n , i;
            double p , q;
            scanf("%d%lf%lf" , &n , &p , &q);
            memset(f , 0 , sizeof(f));
            memset(g , 0 , sizeof(g));
            if(n > 1000)
                n = 1000;
            f[0] = 0;
            g[0] = 1;
            for(i = 1 ; i <= n ; i ++ )
            {
                if(f[i - 1] > g[i - 1])
                    p = 1 - p , q = 1 - q;
                f[i] = (p * g[i - 1] + (1 - p) * q * f[i - 1]) / (1 - (1 - p) * (1 - q));
                g[i] = (q * f[i - 1] + (1 - q) * p * g[i - 1]) / (1 - (1 - p) * (1 - q));
                if(f[i - 1] > g[i - 1])
                    p = 1 - p , q = 1 - q;
            }
            printf("%.6lf
    " , f[n]);
        }
        return 0;
    }
  • 相关阅读:
    windows mysql 的myini
    NuGet 程序源包
    链表更新
    程序包需要 NuGet 客户端版本“XXXXX”或更高版本,但当前的 NuGet 版本为“XXXXXXXXXX”
    chrome下调试安卓app 之 ionic
    ionic3 在ios9.0 系统下 会出现ReferenceError:Can't find variable:Intl 错误提示
    抓取html 生成图片
    grunt 打包 分解(并非原创)
    关于 vue 日期格式的过滤
    Android Studio
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/6203870.html
Copyright © 2020-2023  润新知