测试地址:亚瑟王
做法:本题需要用到概率DP。
一开始本人是这样定义状态的:令
f(i,j)
f
(
i
,
j
)
为第
i
i
轮取到第张牌的概率,然后递推乱搞。然后就连样例都过不去,因为某些概率间是有关联的,而不是相互独立的,例如一轮只能取一张牌,一张牌只能取一次之类的这种东西。
正确的方法是,求出
g(i)
g
(
i
)
,表示第
i
i
张牌在所有轮中被取的概率,显然有:
g(1)=1−(1−p1)r
g
(
1
)
=
1
−
(
1
−
p
1
)
r
考虑求
g(2)
g
(
2
)
,当第一张牌被取过时,因为它被取的那一轮第二张牌肯定不能被取,所以第二张牌被取的概率为
1−(1−p2)r−1
1
−
(
1
−
p
2
)
r
−
1
,当第一张牌没有被取过时,第二张牌被取得概率就是
1−(1−p2)r
1
−
(
1
−
p
2
)
r
。
拓展到求
g(i)
g
(
i
)
的情况,我们发现第
i
i
张牌取的概率,和且只和第到
i−1
i
−
1
张牌中已取的牌的数量有关,那么我们令
f(i,j)
f
(
i
,
j
)
为第
1
1
到张牌取了恰好
j
j
张的概率,状态转移就比较简单了,考虑当前牌取或不取:
如果取第张牌,则有:
f(i,j)+=(1−(1−pi)r−j+1)f(i−1,j−1)
f
(
i
,
j
)
+
=
(
1
−
(
1
−
p
i
)
r
−
j
+
1
)
f
(
i
−
1
,
j
−
1
)
如果不取第
i
i
张牌,则有:
那么
g(i)
g
(
i
)
既然是第
i
i
张牌被取的概率,那它显然就等于上面第一种状态转移的贡献和。
这样一来我们就解决了这个问题,预处理就可以做到
O(Tnr)
O
(
T
n
r
)
的复杂度了。
以下是本人代码:
#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;
}