[学習記録]AtCoder EDP I-Coins[Go]
問題
N を正の奇数とします。
N 枚のコインがあります。 コインには 1,2,…,N と番号が振られています。 各 i (1≤i≤N) について、コイン i を投げると、確率 pi で表が出て、確率 1−pi で裏が出ます。
太郎君は N 枚のコインをすべて投げました。 このとき、表の個数が裏の個数を上回る確率を求めてください。
解法
コインがi 枚目が表の時、裏の時を全て計算していると、
保持する値の数が 2 ^N となり、不可能。
i 枚目までのコインを使って、j 枚表が出る確率を保持すると、計算量もN ^2となり、可能になる。
// i番目までで j枚表が出る
for i := 1; i <= n; i++ {
dp[i] = make([]float64, n+1)
for j := 0; j <= n; j++ {
if i < j {
// i 枚までしか使っていないので、i < j となることはない
break
}
pri := sli[i-1]
// 確率の計算
}
}
「i枚目まで使って、表の数が j 」となる状態への遷移は、
1.「i -1 枚目まで使って、表の数が j -1」から表がでる
2.「「i -1 枚目まで使って、表の数が j」から裏がでる
の二通りがある。ただし、「i枚目まで使って、表の数がゼロ」となる状態への遷移は、
「i - 1 枚目まで使って、表の数がゼロ」からのみである。
「i -1 枚目まで使って、表の数が j -1」から表がでる確率は、条件付き確率として求められる
pri := sli[i-1]
if j == 0 {
// i-1番目で0枚の表が出た確率 × i番目で裏がでる確率
dp[i][j] = dp[i-1][j] * (1.0 - pri)
} else {
// i-1番目で、j-1枚の表が出た確率 × i番目の表が出る確率
t := dp[i-1][j-1] * pri
// i-1番目で、j枚の表が出た確率 × i番目の裏が出る確率
f := dp[i-1][j] * (1.0 - pri)
dp[i][j] = t + f
}
i -1の値を参照することになるが、コインゼロ枚で、表がゼロとなる確率は100%なので、それをdpにセットする。
dp := make([][]float64, n+1)
dp[0] = make([]float64, n+1)
dp[0][0] = 1
計算がおわったとき、dp[n]が、すべてのコインを使った時、表の出る確率のスライスとなっているので、表の数が上回るしきい値を求め、そこからすべて足し合わせる。
ans := 0.0
majority := n/2 + 1
for i := majority; i <= n; i++ {
ans += dp[n][i]
}
コード(AC)
func run() interface{} {
n := readInt()
sli := readSli(n)
dp := make([][]float64, n+1)
dp[0] = make([]float64, n+1)
dp[0][0] = 1
// i番目までで j枚表が出る
for i := 1; i <= n; i++ {
dp[i] = make([]float64, n+1)
for j := 0; j <= n; j++ {
if i < j {
break
}
pri := sli[i-1]
if j == 0 {
// i-1番目で0枚の表が出た確率 × i番目で裏がでる確率
dp[i][j] = dp[i-1][j] * (1.0 - pri)
} else {
// i-1番目で、j-1枚の表が出た確率 × i番目の表が出る確率
t := dp[i-1][j-1] * pri
// i-1番目で、j枚の表が出た確率 × i番目の裏が出る確率
f := dp[i-1][j] * (1.0 - pri)
dp[i][j] = t + f
}
}
}
ans := 0.0
majority := n/2 + 1
for i := majority; i <= n; i++ {
ans += dp[n][i]
}
return ans
}
この記事が気に入ったらサポートをしてみませんか?