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

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

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

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

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

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

那么答案就是

i=0n1fp[i]d[i]

如何求出fp[i]

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

fp[0]=1(1p[i])r

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

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

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

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

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

枚举前i1轮选了j张牌,那么有j轮不会考虑到第i张牌,也就是有rj轮会考虑到第i张牌

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

fp[i]=j=0rf[i1][j](1(1p[i])rj)(i>0)

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

第一种,f[i][j]f[i1][j]转移过来,即第i张牌最终没有选,始终不选第i张牌的概率是(1p[i])rj

f[i][j]+=f[i1][j](1p[i])rj(i>0)

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

这时候,有j1轮没有考虑到第i张牌,所以考虑到第i张牌的轮数是rj+1,最终选择的概率为1(1p[i])rj+1

f[i][j]+=f[i1][j1](1(1p[i])rj+1)(i>0,j>0)

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

因为洛谷上有点卡时,所以预处理了(1p[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\n", 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\n", solve() );
49     }
50     return 0;
51 }
复制代码

 

posted @   mlystdcall  阅读(1847)  评论(3编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
· Manus的开源复刻OpenManus初探
点击右上角即可分享
微信分享提示