【期望DP】BZOJ4008- [HNOI2015]亚瑟王
题目大意
有\(n\)张卡牌,\(r\)轮游戏。每张卡牌只能用至多一次,每张卡牌被用到的概率为\(p_i\)。现在从左往右轮,直到最右一张卡片或者某张卡片被用到。如果某张卡牌被用到,产生\(d_i\)的贡献,回合结束。求期望得分。
思路
神思路。我们用\(f[i,j]\)表示第i张牌得到j个机会的概率(包括被用过后跳掉的)。注意是恰巧得到j个机会,而不是得到至多j个机会或在第j轮被使用到。
对于\(f[i-1.j]\)的转移,我们考虑以下两种情况:
①第i-1张牌也得到了j个机会,并且以此都没有被用到过。则有
\[f[i,j]=f[i-1,j]*(1-p_(i-1))^j
\]
②第i张牌得到了j-1个机会,并且被用到了一次。我们考虑这种情况发生的概率为:
\[p_(i-1)+p_(i-1)*(1-p_(i-1))+…+p_(i-1)*(1-p_(i-1))^j
\]
上式为等比数列,化简后则有:
\[f[i,j]=f[i-1,j+1]*(1-(1-p_(i-1)^j)
\]
所以
\[f[i,j]=f[i-1,j]*(1-p_(i-1))^j+f[i-1,j+1]*(1-(1-p_(i-1)^j)
\]
最后的答案为:
\[ans=∑f[i,j]*(1-(1-p_i)^j)*d_i
\]
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int MAXN=220+5;
const int MAXR=132+5;
double p[MAXN];
int d[MAXN];
double f[MAXN][MAXR];
int n,r;
void init()
{
scanf("%d%d",&n,&r);
for (int i=1;i<=n;i++) scanf("%lf%d",&p[i],&d[i]);
}
void dp()
{
double ans=0;
memset(f,0,sizeof(f));
f[0][r]=1;
for (int i=1;i<=n;i++)
for (int j=1;j<=r;j++)
{
f[i][j]=f[i-1][j]*pow(1-p[i-1],j)+f[i-1][j+1]*(1-pow(1-p[i-1],j+1));
ans+=f[i][j]*(1-pow(1-p[i],j))*d[i];
}
printf("%.10lf\n",ans);
}
int main()
{
int T;
scanf("%d",&T);
while (T--)
{
init();
dp();
}
return 0;
}