【期望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;
}
posted @ 2016-08-17 12:40  iiyiyi  阅读(156)  评论(0编辑  收藏  举报