• 【题解】亚瑟王 HNOI 2015 BZOJ 4008 概率 期望 动态规划


    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4008

    一道不简单的概率和期望dp题

    根据期望的线性性质,容易想到,可以算出每张卡的期望伤害,然后全部加在一起

    手算样例之后发现是正确的,那么我们只要求出每张卡的实际被使用的概率就可以了

    记第$i$张卡的实际被使用的概率为$fp[i]$

    那么答案就是

    $Largesumlimits_{i=0}^{n-1}fp[i]cdot d[i]$

    如何求出$fp[i]$?

    首先考虑第一张卡的$fp$,也就是$fp[0]$,应该为

    $Large fp[0]=1-(1-p[i])^{r}$

    这个很容易理解,因为$(1-p[i])^r$就是这张卡从头到尾始终憋着不出的概率

    那么对于后面的$fp$应该怎么求呢

    有个条件很烦人,就是在每一轮中,出了一张卡的时候立即结束该轮

    那么下面就轮到dp上场啦!

    令$f[i][j]$表示在所有的$r$轮中,前$i$张卡中一共出了$j$张的概率,那么就可以用$O(n)$的时间算出$fp[i](i>0)$

    枚举前$i-1$轮选了$j$张牌,那么有$j$轮不会考虑到第$i$张牌,也就是有$r-j$轮会考虑到第$i$张牌

    那么根据上面的分析,$1-(1-p[i])^{r-j}$就是在$r-j$轮中使用过第$i$张牌的概率,式子:

    $Large fp[i]=sumlimits_{j=0}^{r}f[i-1][j]cdot(1-(1-p[i])^{r-j})(i>0)$

    接下来只要写出$f[i][j]$的转移方程就好了,分两种情况讨论

    第一种,$f[i][j]$从$f[i-1][j]$转移过来,即第$i$张牌最终没有选,始终不选第$i$张牌的概率是$(1-p[i])^{r-j}$

    $Large f[i][j]+=f[i-1][j]cdot(1-p[i])^{r-j}(i>0)$

    第二种,当$j>0$时,$f[i][j]$可以从$f[i-1][j-1]$转移过来,表示最终选择了第$i$张牌

    这时候,有$j-1$轮没有考虑到第$i$张牌,所以考虑到第$i$张牌的轮数是$r-j+1$,最终选择的概率为$1-(1-p[i])^{r-j+1}$

    $Large f[i][j]+=f[i-1][j-1]cdot(1-(1-p[i])^{r-j+1})(i>0,j>0)$

    然后就没了,总时间复杂度$O(Tnr)$,具体细节看代码

    因为洛谷上有点卡时,所以预处理了$(1-p[i])$的幂

     1 #include <cstring>
     2 #include <algorithm>
     3 #include <cstdio>
     4 
     5 using namespace std;
     6 const int MAXN = 223;
     7 const int MAXR = 135;
     8 
     9 int n, r, d[MAXN];
    10 double p[MAXN], fp[MAXN];
    11 
    12 double pow1p[MAXN][MAXN]; // pow1p[i][j]表示(1-p[i])^j
    13 void prelude() {
    14     for( int i = 0; i < n; ++i ) {
    15         pow1p[i][0] = 1;
    16         for( int j = 1; j <= r; ++j )
    17             pow1p[i][j] = pow1p[i][j-1] * (1-p[i]);
    18     }
    19 }
    20 
    21 double f[MAXN][MAXR];
    22 double solve() {
    23     memset( f, 0, sizeof(f) );
    24     memset( fp, 0, sizeof(fp) );
    25     f[0][0] = pow1p[0][r]; // 边界
    26     f[0][1] = fp[0] = 1-f[0][0];
    27     for( int i = 1; i < n; ++i ) {
    28         for( int j = 0; j <= r; ++j ) {
    29             fp[i] += f[i-1][j] * (1 - pow1p[i][r-j]); // 根据f计算fp
    30             f[i][j] += f[i-1][j] * pow1p[i][r-j]; // 不选第i张
    31             if( j ) f[i][j] += f[i-1][j-1] * (1 - pow1p[i][r-j+1]); // 选第i张
    32         }
    33     }
    34     double rtn = 0;
    35     for( int i = 0; i < n; ++i ) {
    36         // printf( "fp[%d] = %lf
    ", i, fp[i] );
    37         rtn += d[i] * fp[i];
    38     }
    39     return rtn;
    40 }
    41 
    42 int main() {
    43     int T; scanf( "%d", &T );
    44     while( T-- ) {
    45         scanf( "%d%d", &n, &r );
    46         for( int i = 0; i < n; ++i ) scanf( "%lf%d", p+i, d+i );
    47         prelude();
    48         printf( "%.10lf
    ", solve() );
    49     }
    50     return 0;
    51 }
  • 相关阅读:
    解析ASP.NET Mvc开发之删除修改数据
    JavaScript module pattern精髓
    Remote验证及其改进(附源码)
    图模型的精确推理
    C#在泛型类中,通过表达式树构造lambda表达式
    类管理指针成员
    朴素贝页斯分类法
    使用Repository模式构建数据库访问层
    利用MVC的过滤器实现url的参数加密和解密
    在多线程中使用静态方法是否有线程安全问题
  • 原文地址:https://www.cnblogs.com/mlystdcall/p/6661858.html
Copyright © 2020-2023  润新知