zoukankan      html  css  js  c++  java
  • 【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;
    }
  • 相关阅读:
    Linnia学习记录
    漫漫考研路
    ENS的学习记录
    KnockoutJS 3.X API 第四章 数据绑定(4) 控制流with绑定
    KnockoutJS 3.X API 第四章 数据绑定(3) 控制流if绑定和ifnot绑定
    KnockoutJS 3.X API 第四章 数据绑定(2) 控制流foreach绑定
    KnockoutJS 3.X API 第四章 数据绑定(1) 文本及样式绑定
    KnockoutJS 3.X API 第三章 计算监控属性(5) 参考手册
    KnockoutJS 3.X API 第三章 计算监控属性(4)Pure computed observables
    KnockoutJS 3.X API 第三章 计算监控属性(3) KO如何实现依赖追踪
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/6203870.html
Copyright © 2011-2022 走看看