[HNOI2015]亚瑟王(概率期望,DP)
题目大意:很清晰了,不写了。
$1\le T\le 444,1\le n\le 220,0\le r\le 132,0<p_i<1,0\le d_i\le 1000$。
$p_i$ 和 $d_i$ 随机生成。(然而没什么用)
一直以来还是zz的题最良心。
有个很讨厌的地方是 $r=0$,先判掉。什么mdzz出题人
考虑第 $i$ 张牌最后被翻开的概率 $f_i$。答案为 $\sum f_id_i$。
那 $f_i$ 怎么求?上DP。(smg啊……)
我们把 $r$ 次操作合在一起考虑。$dp[i][j]$ 表示在前 $i$ 张牌中,已经有 $j$ 张被翻开的概率。
$dp[i][j]=dp[i-1][j](1-p_i)^{r-j}+dp[i-1][j-1](1-(1-p_i)^{r-j+1})$。
解释一下,前面是这张牌不被翻开的概率,因为还有 $r-j$ 次可能的翻牌,所以这几次都不能被翻开。后面是被翻开的概率,同理。
初始 $dp[1][0]=(1-p_i)^r,dp[1][1]=1-(1-p_i)^r$。
接下来就可以搞 $f_i$ 了。$f_i=\sum dp[i-1][j-1](1-(1-p_i)^{r-j})$。原理和上面一样。
如果预处理每个 $1-p_i$ 从 $0$ 到 $r$ 次方的幂,可以做到 $O(Tnr)$。
#include<bits/stdc++.h> using namespace std; #define FOR(i,a,b) for(int i=(a);i<=(b);i++) #define ROF(i,a,b) for(int i=(a);i>=(b);i--) #define MEM(x,v) memset(x,v,sizeof(x)) inline int read(){ char ch=getchar();int x=0,f=0; while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar(); while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar(); return f?-x:x; } int t,n,r; double p[233],d[233],f[233],dp[233][144],pw[233][144],ans; int main(){ t=read(); while(t--){ MEM(dp,0);MEM(f,0); n=read();r=read(); FOR(i,1,n){ scanf("%lf%lf",p+i,d+i); pw[i][0]=1; FOR(j,1,r) pw[i][j]=pw[i][j-1]*(1-p[i]); } if(!r){puts("0.0000000000");continue;} dp[1][0]=pw[1][r]; dp[1][1]=f[1]=1-dp[1][0]; FOR(i,2,n) FOR(j,0,min(i,r)){ if(i!=j) dp[i][j]+=dp[i-1][j]*pw[i][r-j]; if(j) dp[i][j]+=dp[i-1][j-1]*(1-pw[i][r-j+1]); } FOR(i,2,n) FOR(j,1,min(i,r)) f[i]+=dp[i-1][j-1]*(1-pw[i][r-j+1]); ans=0; FOR(i,1,n) ans+=f[i]*d[i]; printf("%.10lf\n",ans); } }