题意
传送门 POJ 2151
题解
设事件
A
A
A 为每支队伍至少通过一题,事件
B
B
B 为每只队伍通过题目的数量在区间
[
1
,
n
−
1
]
[1,n-1]
[1,n−1] 内。各队伍通过的题目数的事件相互独立。
P
(
A
)
=
∏
0
≤
i
<
T
(
1
−
∏
0
≤
j
<
M
(
1
−
p
i
j
)
)
P(A)=\prod_{0\leq i<T}(1-\prod_{0\leq j<M}(1-p_{ij}))
P(A)=0≤i<T∏(1−0≤j<M∏(1−pij))
d
p
[
i
]
[
j
]
[
k
]
dp[i][j][k]
dp[i][j][k] 代表队伍
i
i
i 在
[
0
,
j
]
[0,j]
[0,j] 题内通过了
k
k
k 题的概率。对于题目
j
j
j,有通过与不通过两种可能,则有递推式
d
p
[
i
]
[
j
]
[
k
]
=
d
p
[
i
]
[
j
−
1
]
[
k
]
∗
(
1
−
p
[
i
]
[
j
]
)
+
d
p
[
i
]
[
j
−
1
]
[
k
−
1
]
∗
p
[
i
]
[
j
]
dp[i][j][k]=dp[i][j-1][k]*(1-p[i][j])+dp[i][j-1][k-1]*p[i][j]
dp[i][j][k]=dp[i][j−1][k]∗(1−p[i][j])+dp[i][j−1][k−1]∗p[i][j]则有
P
(
B
)
=
∏
0
≤
i
<
T
(
∑
1
≤
k
<
n
d
p
[
i
]
[
M
−
1
]
[
k
]
)
P(B)=\prod_{0\leq i<T}(\sum_{1\leq k<n}dp[i][M-1][k])
P(B)=0≤i<T∏(1≤k<n∑dp[i][M−1][k])
D
P
DP
DP 第一维可压缩,实现上使用滚动数组。答案即
P
(
A
)
−
P
(
B
)
P(A)-P(B)
P(A)−P(B)
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxt 1005
#define maxm 35
int M, T, N;
double p[maxt][maxm], dp[2][maxm];
int main()
{
while (~scanf("%d%d%d", &M, &T, &N) && (M | T | N))
{
for (int i = 0; i < T; i++)
{
for (int j = 0; j < M; j++)
{
scanf("%lf", &p[i][j]);
}
}
// 每只队伍至少通过1题的概率, 每只队伍通过1~n-1题的概率
double p1 = 1, p2 = 1;
for (int i = 0; i < T; i++)
{
double tmp = 1;
for (int j = 0; j < M; j++)
{
tmp *= 1 - p[i][j];
}
p1 *= 1 - tmp;
}
for (int i = 0; i < T; i++)
{
memset(dp, 0, sizeof(dp));
double *crt = dp[0], *nxt = dp[1];
crt[0] = 1;
for (int j = 0; j < M; j++)
{
nxt[0] = crt[0] * (1 - p[i][j]);
for (int k = 1; k <= j + 1; k++)
{
nxt[k] = crt[k] * (1 - p[i][j]) + crt[k - 1] * p[i][j];
}
swap(crt, nxt);
}
double tmp = 0;
for (int j = 1; j < N; j++)
{
tmp += crt[j];
}
p2 *= tmp;
}
printf("%.3lf\n", p1 - p2);
}
return 0;
}