BZOJ 4008: [HNOI2015]亚瑟王 [DP 概率 !!!]

传送门

题意:

$r$轮$n$张卡牌,每一轮依次考虑每张卡牌,$p_i$概率发动造成$d_i$伤害后结束本轮或者继续考虑下一张

每张卡牌发动过之后以后都会跳过

求$r$轮之后的期望伤害


 

看了一节课出题人的做法,并不知道该怎么写代码,感觉带着除法精度好玄学....

发现网上的题解都是另一种做法

$f[i][j]$表示第$i$张牌被考虑了$j$次的概率

有两个转移:

$1.\ $上一张牌考虑了$j$次都不发动

$2.\ $上一张牌考虑了$j+1$次,之前$k$次不发动,第$k$次发动了,$a*\sum\limits_{k=0}^{j}{(1-a)^k}$等比数列求和

$f[i][j]=f[i-1][j]*(1-p_{i-1})^j\ +\ f[i-1][j+1]*(1-(1-p_{i-1})^{j+1})$

 

我现在还不太明白两种做法有什么联系

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=225;
typedef double ld;
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
int n,r,d[N];
double x;
ld p[N],f[N][N];
ld g[N][N];
int main(){
    freopen("in","r",stdin);
    int T=read();
    while(T--){
        //memset(f,0,sizeof(f));
        n=read();r=read();
        for(int i=1;i<=n;i++) 
            scanf("%lf",&x),p[i]=x,d[i]=read();
        memset(f[0],0,sizeof(f[0]));
        f[0][r]=1;
        ld ans=0;
        for(int i=1;i<=r;i++) g[0][i]=1;
        for(int i=1;i<=n;i++) g[i][0]=1;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=r;j++){
                f[i][j]=f[i-1][j]*g[i-1][j];
                if(j+1<=r) f[i][j]+=f[i-1][j+1]*( 1-g[i-1][j+1] );
                g[i][j]=g[i][j-1]*(1-p[i]);
                ans+=f[i][j]*( 1-g[i][j] )*d[i];
            }
        printf("%.10lf\n",(double)ans);
    }
}

 

posted @ 2017-03-05 11:59  Candy?  阅读(282)  评论(0编辑  收藏  举报