(一)题目
一个软件有s个子系统,会产生n种bug,某人一天发现一个bug,这个bug属于一个子系统,属于一个分类,每个bug属于某个子系统的概率是 1 s \frac{1}{s} s1,属于某种分类的概率是 1 n \frac{1}{n} n1,问发现n种bug,每个子系统都发现bug的天数的期望
(二)状态设计 以及 答案分析
首先由上一道题的思路,我们很容易想到设dp[i][j]
表示已经发现了i
种错误、分别属于j
个系统,还有几天到达n
种bug,s
个系统都发现bug的状态
然后我们发现dp[n][s]
(即n
种bug,s
个系统都发现bug)显然为0
,答案也很明显在dp[0][0]
(什么bug都没发现)
(三)转移
1. 初步的错误思路
现在已经发现了i种bug,分属于j个系统
,那么前一天一般情况下则可以有以下四种状态
{
d
p
[
i
]
[
j
]
(这一天发现了
b
u
g
但是没有影响进度)
d
p
[
i
+
1
]
[
j
]
(这一天的
b
u
g
是一个新的种类)
d
p
[
i
]
[
j
+
1
]
(这一天的
b
u
g
在一个新的系统)
d
p
[
i
+
1
]
[
j
+
1
]
(这一天的
b
u
g
在一个新的系统且是一个新的种类)
\begin{cases} dp[i][j](这一天发现了bug但是没有影响进度)\\ dp[i + 1][j](这一天的bug是一个新的种类)\\ dp[i][j + 1](这一天的bug在一个新的系统) \\ dp[i + 1][j + 1] (这一天的bug在一个新的系统且是一个新的种类) \end{cases}
⎩
⎨
⎧dp[i][j](这一天发现了bug但是没有影响进度)dp[i+1][j](这一天的bug是一个新的种类)dp[i][j+1](这一天的bug在一个新的系统)dp[i+1][j+1](这一天的bug在一个新的系统且是一个新的种类)
那么是不是就有
d
p
[
i
]
[
j
]
=
d
p
[
i
]
[
j
]
+
d
p
[
i
]
[
j
+
1
]
+
d
p
[
i
+
1
]
[
j
]
+
d
p
[
i
+
1
]
[
j
+
1
]
4
+
1
dp[i][j] = \frac{dp[i][j] + dp[i][j + 1] + dp[i + 1][j] + dp[i + 1][j + 1]}{4}+ 1
dp[i][j]=4dp[i][j]+dp[i][j+1]+dp[i+1][j]+dp[i+1][j+1]+1了呢?
2.正确的思路
以上分析没有问题,但是忽略了一个重要的因素概率,很明显,这一天的bug在一个新的系统且是一个新的种类和这一天的bug是一个新的种类的概率是不一样的嘛,怎么能用等概率事件的方法计算期望呢?而且以上四种状态中的一些可能不存在,比如(当
i
+
1
>
n
i + 1 > n
i+1>n 或
j
+
1
>
s
j + 1 > s
j+1>s的情况)
所以在这个基础上,我们转移的时候还要给前驱状态乘上概率
形式化一点:
设以上四种事件发生的概率分别为
p
1
,
p
2
,
p
3
,
p
4
,
则
d
p
[
i
]
[
j
]
=
p
1
×
d
p
[
i
]
[
j
]
+
p
2
×
d
p
[
i
+
1
]
[
j
]
+
p
3
×
d
p
[
i
]
[
j
+
1
]
+
p
4
×
d
p
[
i
+
1
]
[
j
]
+
1
设以上四种事件发生的概率分别为p_1,p_2,p_3,p_4,则 \\ dp[i][j] = p_1 \times dp[i][j] + p_2 \times dp[i + 1][j] + p_3 \times dp[i][j + 1] + p_4 \times dp[i + 1][j] + 1 \\
设以上四种事件发生的概率分别为p1,p2,p3,p4,则dp[i][j]=p1×dp[i][j]+p2×dp[i+1][j]+p3×dp[i][j+1]+p4×dp[i+1][j]+1
但是这样还是无法转移,因为无法从dp[i][j]
这个位置状态转移到dp[i][j]
这个未知状态
但是没有关系,移下项就好了嘛
d
p
[
i
]
[
j
]
−
p
1
×
d
p
[
i
]
[
j
]
=
p
2
×
d
p
[
i
+
1
]
[
j
]
+
p
3
×
d
p
[
i
]
[
j
+
1
]
+
p
4
×
d
p
[
i
+
1
]
[
j
+
1
]
+
1
d
p
[
i
]
[
j
]
=
p
2
×
d
p
[
i
+
1
]
[
j
]
+
p
3
×
d
p
[
i
]
[
j
+
1
]
+
p
4
×
d
p
[
i
+
1
]
[
j
+
1
]
+
1
1
−
p
1
dp[i][j] - p_1 \times dp[i][j] = p_2 \times dp[i + 1][j] + p_3 \times dp[i][j + 1] + p_4 \times dp[i + 1][j + 1] + 1 \\ dp[i][j] = \frac{p_2 \times dp[i + 1][j] + p_3 \times dp[i][j + 1] + p_4 \times dp[i + 1][j + 1] + 1}{1 - p1}
dp[i][j]−p1×dp[i][j]=p2×dp[i+1][j]+p3×dp[i][j+1]+p4×dp[i+1][j+1]+1dp[i][j]=1−p1p2×dp[i+1][j]+p3×dp[i][j+1]+p4×dp[i+1][j+1]+1
3.计算概率
现在只要求出
p
1
,
p
2
,
p
3
,
p
4
p_1,p_2,p_3,p_4
p1,p2,p3,p4就好了
显然
p
1
=
i
n
×
j
s
p_1 = \frac{i}{n} \times \frac{j}{s}
p1=ni×sj 即分步的乘法原理,在
n
n
n种可能的状态中有
i
i
i个被选过的满足,在
s
s
s个可能的系统中有
j
j
j个被选过的满足
p
2
=
n
−
i
n
×
j
s
p_2 = \frac{n - i}{n} \times \frac{j}{s}
p2=nn−i×sj 同理,只不过在
n
n
n种可能的状态中有
n
−
i
n - i
n−i个没有被选过的满足
p
3
=
i
n
×
s
−
j
s
p_3 = \frac{i}{n} \times \frac{s - j}{s}
p3=ni×ss−j同理,只不过在
s
s
s种可能的系统中有
s
−
j
s - j
s−j个没有被选过的满足
p
4
=
n
−
i
n
×
s
−
j
s
p_4 = \frac{n - i}{n} \times \frac{s - j}{s}
p4=nn−i×ss−j乘法原理,在
n
n
n种可能的系统中有
n
−
i
n - i
n−i个没有被选过的满足,在
s
s
s种可能的系统中有
s
−
j
s - j
s−j个没有被选过的满足
这样就大功告成了
因为我们是已知最后状态dp[n][s] = 0
,倒推dp[0][0]
所写记忆化搜索方便些
(四)代码实现
思路前面讲的已经很清楚了,代码自己理解一下就好了
#include <bits/stdc++.h>
using namespace std;
const int N = 1000;
int n, s;
double dp[N + 5][N + 5];
double dfs(int i, int j) {
if (i == n && j == s) return 0; // 到达目标状态
if (dp[i][j] != 0) return dp[i][j];
dp[i][j] = 1;
if (i + 1 <= n) dp[i][j] += dfs(i + 1, j) * (n - i) * 1.0 * j / (n * s);
if (j + 1 <= s) dp[i][j] += dfs(i, j + 1) * i * (s - j) / (1.0 * n * s);
if (i + 1 <= n && j + 1 <= s) dp[i][j] += dfs(i + 1, j + 1) * (n - i) * 1.0 * (s - j) / (n * s);
dp[i][j] /= (1.0 - i * j * 1.0 / (n * s));
return dp[i][j];
}
int main() {
scanf("%d%d", &n, &s);
printf("%.4lf\n", dfs(0, 0));
return 0;
}