問題
J – Sushi
期待値DP
期待値DPについて以下分かりやすいです。
また、Pythonであるということ以外、メモ化再帰は以下のほぼ写経です。
Educational DP Contest の F ~ J 問題の解説と類題集 J 問題 – Sushi
回答
メモ化再帰
PyPyでもTLEで間に合わない。
import sys
# input処理を高速化する
input = sys.stdin.readline
# 許容する再帰処理の回数を変更
sys.setrecursionlimit(300*300*300+10)
def main():
N = int(input())
lst_a = list(map(int, input().split()))
one, two, three = lst_a.count(1), lst_a.count(2), lst_a.count(3)
# dp[i][j][k] := 残り1個の皿がi枚、2個の皿がj枚、3個の皿がk枚の状態から、
# 寿司をすべてなくすのに必要な操作回数の期待値
# dpは-1で初期化
dp = [[[-1.0] * (N+1) for _ in range(N+1)] for _ in range(N+1)]
def rec(i, j, k):
if dp[i][j][k] >= 0:
return dp[i][j][k]
if (i == 0 and j == 0 and k == 0):
return 0.0
res = 0.0
if i > 0:
res += rec(i-1, j, k) * i
if j > 0:
res += rec(i+1, j-1, k) * j
if k > 0:
res += rec(i, j+1, k-1) * k
res += N
res *= 1.0 / (i + j + k)
dp[i][j][k] = res
return dp[i][j][k]
print(rec(one, two, three))
main()
DP
メモ化再帰をDPに書き直したもの。これはPyPyであれば間に合う。
import sys
# input処理を高速化する
input = sys.stdin.readline
def main():
N = int(input())
lst_a = list(map(int, input().split()))
one, two, three = lst_a.count(1), lst_a.count(2), lst_a.count(3)
# dp[i][j][k] := 残り1個の皿がi枚、2個の皿がj枚、3個の皿がk枚の状態から、
# 寿司をすべてなくすのに必要な操作回数の期待値
dp = [[[0.0] * (N+1) for _ in range(N+1)] for _ in range(N+1)]
for k in range(three + 1):
for j in range(two + three + 1 - k):
for i in range(one + two + three + 1 - k -j):
if i == 0 and j == 0 and k == 0:
continue
tmp = N * 1.0
if i > 0:
tmp += i * dp[i -1][j][k]
if j > 0:
tmp += j * dp[i + 1][j - 1][k]
if k > 0:
tmp += k * dp[i][j + 1][k -1]
dp[i][j][k] = tmp / (i + j + k)
print(dp[one][two][three])
main()