【BZOJ4008】亚瑟王(HNOI2015)-概率DP
测试地址:亚瑟王
做法:本题需要用到概率DP。
一开始本人是这样定义状态的:令为第轮取到第张牌的概率,然后递推乱搞。然后就连样例都过不去,因为某些概率间是有关联的,而不是相互独立的,例如一轮只能取一张牌,一张牌只能取一次之类的这种东西。
正确的方法是,求出,表示第张牌在所有轮中被取的概率,显然有:
考虑求,当第一张牌被取过时,因为它被取的那一轮第二张牌肯定不能被取,所以第二张牌被取的概率为,当第一张牌没有被取过时,第二张牌被取得概率就是。
拓展到求的情况,我们发现第张牌取的概率,和且只和第到张牌中已取的牌的数量有关,那么我们令为第到张牌取了恰好张的概率,状态转移就比较简单了,考虑当前牌取或不取:
如果取第张牌,则有:
如果不取第张牌,则有:
那么既然是第张牌被取的概率,那它显然就等于上面第一种状态转移的贡献和。
这样一来我们就解决了这个问题,预处理就可以做到的复杂度了。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
int T,n,r;
long double p[510],d[510],power[510][510];
long double sumr[510]={0},sumc[510]={0},f[510][510]={0};
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&r);
for(int i=1;i<=n;i++)
{
scanf("%Lf%Lf",&p[i],&d[i]);
power[i][0]=1.0;
for(int j=1;j<=r;j++)
power[i][j]=power[i][j-1]*(1-p[i]);
}
f[0][0]=1.0;
for(int i=1;i<=n;i++)
for(int j=0;j<=min(i,r);j++)
{
f[i][j]=0.0;
if (j>0) f[i][j]+=f[i-1][j-1]*(1-power[i][r-j+1]);
if (j<i) f[i][j]+=f[i-1][j]*power[i][r-j];
}
long double ans=0.0,sum;
for(int i=1;i<=n;i++)
{
sum=0.0;
for(int j=0;j<=min(i-1,r);j++)
sum+=f[i-1][j]*(1-power[i][r-j]);
ans+=sum*d[i];
}
printf("%.10Lf\n",ans);
}
return 0;
}