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); } }
Copyright:http://www.cnblogs.com/candy99/