BZOJ 4008: [HNOI2015]亚瑟王
略显麻烦并且套路的概率DP题,被一个初始化坑了好久
首先根据期望的线性性我们可以求出每张卡使用的概率然后计算答案
我们先不谈这个概率怎么求,先来想一个一眼能设出来的DP,令\(f_{i,j}\)表示前\(i\)张牌里选\(j\)张的概率
考虑它怎么转移,显然是从\(f_{i-1,j}\)和\(f_{i-1,j-1}\)转移过来,然后我们发现按这个顺序DP有一个好处就是一张牌前面有多少张牌被发动了是确定的
那么这张牌被考虑的次数显然也可以统计出来了,容易得到此时的转移方程
\[f_{i,j}=f_{i,j}+f_{i-1,j}\times (1-p_i)^{r-j}
\]
\[f_{i,j}=f_{i,j}+f_{i-1,j-1}\times [1-(1-p_i)^{r-j+1}]
\]
那么我们求出了看似没用的\(f_{i,j}\)能干嘛呢,考虑我们推导\(f_{i,j}\)的时候根据一张牌前面有多少张发动了技能就可以推出这张牌被考虑的次数,那么我们同理可以推导出答案:
\[P(i)=\sum_{j=0}^r f_{i-1,j}\times [1-(1-p_i)^{r-j}]
\]
注意关键的边界:第一张牌!稍加分析我们有:
\[f_{1,0}=(1-p_i)^r;P(1)=f_{1,1}=1-(1-p_i)^r
\]
那么这道题就算是做完了,复杂度\(O(t\times n\times r)\)
#include<cstdio>
#define RI register int
#define CI const int&
using namespace std;
const int N=225,M=135;
int t,n,m,d[N]; double ans,tp,p[N][M],f[N][N];
int main()
{
for (scanf("%d",&t);t;--t)
{
RI i,j; for (scanf("%d%d",&n,&m),i=1;i<=n;++i)
scanf("%lf%d",&tp,&d[i]),p[i][0]=1,p[i][1]=1.0-tp;
for (i=1;i<=n;++i) for (j=2;j<=m;++j) p[i][j]=p[i][j-1]*p[i][1];
for (f[1][0]=p[1][m],f[1][1]=1.0-p[1][m],i=2;i<=n;++i) for (j=0;j<=m;++j)
f[i][j]=f[i-1][j]*p[i][m-j]+(j?f[i-1][j-1]*(1.0-p[i][m-j+1]):0);
for (ans=(1.0-p[1][m])*d[1],i=2;i<=n;++i)
{
for (tp=j=0;j<=m;++j) tp+=f[i-1][j]*(1.0-p[i][m-j]);
ans+=1.0*tp*d[i];
}
printf("%.10lf\n",ans);
}
return 0;
}
辣鸡老年选手AFO在即