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;
    }
  • 相关阅读:
    将不确定变成确定~LINQ DBML模型可以对应多个数据库吗
    将不确定变成确定~frameset页面不能正确加载
    System.Web.Caching.Cache删除某键后,希望同时触发其它动作(关键时刻,还是事件靠的住)
    Linq实体类的设计(解决了复合查询的问题,同时解决了LINQ上下文缓存问题)
    将不确定变成确定~LINQ查询两种写法,性能没有影响,优化查询应该是“按需查询”
    ASP.NET的内置对象
    Templating with JSF 2.0 Facelets
    IOS设计模式学习(1)设计模式初窥
    20个强大的jQuery翻书插件【 jQuery flipbook】
    linux网络编程之socket(十四):基于UDP协议的网络程序
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/6203870.html
Copyright © 2011-2022 走看看