[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);
    }
}
View Code

猜你喜欢

转载自www.cnblogs.com/1000Suns/p/10922089.html