【bzoj4008】[HNOI2015]亚瑟王 概率dp
题目描述
$n$ 张牌,$r$ 轮游戏,每轮从左向右操作,遇到第 $i$ 张牌有 $p_i$ 的概率选中,选中会产生 $d_i$ 的贡献,丢弃掉该牌并结束这一轮,否则继续下一张。问最终的期望贡献。
输入
输入文件的第一行包含一个整数 T,代表测试数据组数。
接下来一共 T 组数据。
每组数据的第一行包含两个用空格分开的整数 n 和 r ,分别代表卡牌的张数和游戏的轮数。
接下来 n 行,每行包含一个实数和一个整数,由空格隔开,描述一张卡牌。第 i 行的两个数为 pi 和 di ,分别代表第 i 张卡牌技能发动的概率(实数)和技能发动造成的伤害(整数)。保证 pi 最多包含 4 位小数,且为一个合法的概率。
输出
对于每组数据,输出一行,包含一个实数,为这套卡牌在这一局游戏中造成的伤害的期望值。对于每一行输出,只有当你的输出和标准答案的相对误差不超过10^-8时——即|a-o|/a<=10-8时(其中a是标准答案,o是输出),你的输出才会被判为正确。
建议输出10 位小数。
样例输入
1
3 2
0.5000 2
0.3000 3
0.9000 1
样例输出
3.2660250000
题解
概率dp
这种神题像我这种傻逼大概一辈子也想不出来。。。
考虑将这 $r$ 次操作一起进行,设 $f[i][j]$ 表示前 $i$ 个人,还剩 $j$ 次选择机会的概率。
考虑 $f[i][j]$ 的转移:
如果 $i$ 没有被选择,则 $j$ 次机会都不能选中,$f[i][j]=f[i-1][j]·(1-p[i])^j$ ;
如果 $i$ 有被选择,则 $j+1$ 次机会不能都不选中, $f[i][j]=f[i-1][j+1]·(1-(1-p[i])^{j+1})$ 。
因此总的状态转移方程就是 $f[i][j]=f[i-1][j]·(1-p[i])^j+f[i-1][j+1]·(1-(1-p[i])^{j+1})$ 。
得到所有的 $f$ 之后即可统计答案。对于第 $i$ 张牌,它被选中的概率是 $\sum\limits_{j=0}^mf[i-1][j]·(1-(1-p[i])^j)$ ,再乘上 $d[i]$ 即为贡献。
时间复杂度 $O(Tnr)$
#include <cmath> #include <cstdio> double f[230][140] , p[230] , d[230]; int main() { int T; scanf("%d" , &T); while(T -- ) { int n , m , i , j; double ans = 0; scanf("%d%d" , &n , &m); for(i = 1 ; i <= n ; i ++ ) scanf("%lf%lf" , &p[i] , &d[i]); f[0][m] = 1; for(i = 1 ; i <= n ; i ++ ) for(j = 0 ; j <= m ; j ++ ) f[i][j] = f[i - 1][j] * pow(1 - p[i] , j) + f[i - 1][j + 1] * (1 - pow(1 - p[i] , j + 1)); for(i = 1 ; i <= n ; i ++ ) for(j = 0 ; j <= m ; j ++ ) ans += d[i] * f[i - 1][j] * (1 - pow(1 - p[i] , j)); f[0][m] = 0; printf("%.10lf\n" , ans); } return 0; }