题目描述
小欧拿到了一个数组, 她每次随机选择一个元素,然后将该元素以及其后缀全部删除。
已知第
i
i
i 个元素被选到的概率为
a
i
/
s
u
m
aᵢ/sum
ai/sum ,其中
s
u
m
sum
sum 为当前数组所有元素之和。
小欧想知道, 将数组全部删完的期望次数是多少?
输入描述
第一行输入一个正整数
n
n
n ,代表数组的大小。(
1
≤
n
≤
100
1≤n≤100
1≤n≤100)
第二行输入
n
n
n 个正整数
a
i
aᵢ
ai,代表数组的元素。(
1
≤
a
i
≤
1
0
9
1≤aᵢ≤10⁹
1≤ai≤109)
输出描述
一个浮点数,代表最终的期望次数。如果你的答案和正确答案的相对误差不超过10⁻⁵,则认为答案正确。
示例1
样例输入
2
13
样例输出
1.75
说明
25%的概率操作1次, 75%的概率操作2次,总期望次数为
1
∗
0.25
+
2
∗
0.75
=
1.75
1^{ \ast }0.25+2^{ \ast }0.75=1.75
1∗0.25+2∗0.75=1.75
思路:期望动态规划
考虑第一步:我们可以选择 n n n个位置。选完之后删掉后缀,那么显然留下的一定是原数组的一个前缀状态 。
那么考虑这个过程一定是让这个数组慢慢缩短,并且每次缩短,问题结构不发生变化。始终是一个连续的数组。我们可以考虑使用动态规划:
状态
d p i 代表考虑数组 A 的 [ 1 , i ] 的前缀的期望次数 dp_i 代表考虑数组A的[1,i] 的前缀的期望次数 dpi代表考虑数组A的[1,i]的前缀的期望次数
转移
最开始,假设第一步选择了某个位置 j j j , 那么问题就从 A [ 1 , n ] A[1,n] A[1,n] 转化为 子问题 A [ 1 , j − 1 ] A[1,j-1] A[1,j−1] 。 子问题的期望次数为 d p j − 1 dp_{j-1} dpj−1 , 而选到第 j j j个位置的概率根据题目定义是: A [ j ] s u m ( A [ 1 , n ] ) \frac{A[j]}{sum(A[1,n])} sum(A[1,n])A[j] 。
所以选择 j j j的贡献是 d p j − 1 ∗ A [ j ] s u m ( A [ 1 , n ] ) dp_{j-1} * \frac{A[j]}{sum(A[1,n])} dpj−1∗sum(A[1,n])A[j] 。枚举所有 j j j 再求和即为最终答案。
上述分析对于任意一个前缀
A
[
1
,
i
]
A[1,i]
A[1,i] 都满足。所以转移方程为:
d
p
i
=
∑
j
=
1
i
d
p
j
−
1
∗
A
[
j
]
s
u
m
(
A
[
1
,
i
]
)
dp_i = \sum_{j=1}^{i} dp_{j-1} * \frac{A[j]}{sum(A[1,i])}
dpi=j=1∑idpj−1∗sum(A[1,i])A[j]
复杂度为
O
(
n
2
)
O(n^2)
O(n2) , 具体实现细节见代码!
思考问题
如果每次的概率不是变化的。即转移中的 A [ j ] s u m ( A [ 1 , i ] ) → A [ j ] s u m ( A [ 1 , n ] ) \frac{A[j]}{sum(A[1,i])} \rightarrow \frac{A[j]}{sum(A[1,n])} sum(A[1,i])A[j]→sum(A[1,n])A[j] 。 你是否能够设计出一个 O ( n ) O(n) O(n)的算法?
代码
python
# Initialize DP table with length + 5 zeros
length = int(input())
dp = [0] * (length + 5)
# Base case: probability of choosing the first element is 1
dp[1] = 1
# Read in the array
array = list(map(int, input().split()))
# Fill in the DP table
for i in range(2, length + 1):
# Calculate the total sum of the first i elements
total_sum = sum(array[:i])
for j in range(i):
# Calculate the probability of choosing the jth element
probability = array[j] / total_sum
# Update the DP table using the recurrence relation
dp[i] += probability * (dp[j] + 1)
# Print the result
print(dp[length])