題目的難度顏色使用 Luogu 上的分級,由簡單到困難分別為 🔴🟠🟡🟢🔵🟣⚫。
Problem Statement
題目簡述
有 N N N 枚硬幣(N N N 為奇數),第 i i i 枚硬幣投擲出正面的機率為 p i p_i p i 。
求投擲所有硬幣後,正面出現的次數嚴格多於反面出現次數的機率。
Constraints
約束條件
N N N 是奇數且 1 ≤ N ≤ 2999 1 \le N \le 2999 1 ≤ N ≤ 2 9 9 9 。
0 < p i < 1 0 < p_i < 1 0 < p i < 1 。
思路:機率 DP
狀態定義
定義 d p [ i ] [ j ] dp[i][j] d p [ i ] [ j ] 為投擲前 i i i 枚硬幣後,恰好有 j j j 枚為正面的機率。
狀態轉移
考慮第 i i i 枚硬幣的結果:
正面 (機率 p i p_i p i ):需從 d p [ i − 1 ] [ j − 1 ] dp[i-1][j-1] d p [ i − 1 ] [ j − 1 ] 轉移,貢獻 d p [ i − 1 ] [ j − 1 ] × p i dp[i-1][j-1] \times p_i d p [ i − 1 ] [ j − 1 ] × p i 。
反面 (機率 1 − p i 1-p_i 1 − p i ):需從 d p [ i − 1 ] [ j ] dp[i-1][j] d p [ i − 1 ] [ j ] 轉移,貢獻 d p [ i − 1 ] [ j ] × ( 1 − p i ) dp[i-1][j] \times (1-p_i) d p [ i − 1 ] [ j ] × ( 1 − p i ) 。
狀態轉移方程
d p [ i ] [ j ] = d p [ i − 1 ] [ j ] ⋅ ( 1 − p i ) + d p [ i − 1 ] [ j − 1 ] ⋅ p i dp[i][j] = dp[i-1][j] \cdot (1 - p_i) + dp[i-1][j-1] \cdot p_i
d p [ i ] [ j ] = d p [ i − 1 ] [ j ] ⋅ ( 1 − p i ) + d p [ i − 1 ] [ j − 1 ] ⋅ p i
空間優化
由於 d p [ i ] dp[i] d p [ i ] 只依賴 d p [ i − 1 ] dp[i-1] d p [ i − 1 ] ,可使用滾動陣列 優化空間。程式碼中以 defaultdict 實作:
f:儲存上一輪的機率分佈
nf:計算當前輪的機率分佈
答案計算
最終答案為正面數嚴格多於反面數的機率總和,即 j > N − j ⇒ j > N 2 j > N - j \Rightarrow j > \frac{N}{2} j > N − j ⇒ j > 2 N :
Ans = ∑ j = ⌊ N / 2 ⌋ + 1 N d p [ N ] [ j ] \text{Ans} = \sum_{j = \lfloor N/2 \rfloor + 1}^{N} dp[N][j]
Ans = j = ⌊ N / 2 ⌋ + 1 ∑ N d p [ N ] [ j ]
複雜度分析
時間複雜度:O ( N 2 ) \mathcal{O}(N^2) O ( N 2 )
空間複雜度:O ( N ) \mathcal{O}(N) O ( N )
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from collections import defaultdictdef solve (): n = int (input ()) P = list (map (float , input ().split())) f = defaultdict(float ) f[0 ] = 1 for p in P: nf = defaultdict(float ) for j, q in f.items(): nf[j + 1 ] += q * p nf[j] += q * (1 - p) f = nf print (sum (q for j, q in f.items() if j > n - j)) if __name__ == "__main__" : solve()