[学習記録]AtCoder EDP L-Deque[Go]
問題
太郎君と次郎君が次のゲームで勝負します。
最初に、数列 a=(a1,a2,…,aN) が与えられます。 a が空になるまで、二人は次の操作を交互に行います。 先手は太郎君です。
a の先頭要素または末尾要素を取り除く。 取り除いた要素を x とすると、操作を行った人は x 点を得る。
ゲーム終了時の太郎君の総得点を X、次郎君の総得点を Y とします。 太郎君は X−Y を最大化しようとし、次郎君は X−Y を最小化しようとします。
二人が最適に行動すると仮定したとき、X−Y を求めてください。
解法
問題を見て、DPの添字として持てそうなものは、残っている要素のインデックスがある。
残っている要素のインデックスが l , r の時、どっちを取ったほうが得かを全ての l , r について計算すれば答えになる。
そのため、 残りが l , r のときの点差をDPに記録していく。
同じ状態を計算しないようにメモを取っておく
func run() interface{} {
n := readInt()
sli = readSli(n)
dp = make([][]int, n+1)
memo = make([][]bool, n+1)
for i := 0; i <= n; i++ {
dp[i] = make([]int, n+1)
memo[i] = make([]bool, n+1)
}
// l, rのインデックスを添字もつDPを考える
ans := calculate(0, n-1)
return ans
}
「太郎君は X−Y を最大化しようとし、次郎君は X−Y を最小化しようとします。」
これを言い換えると、
「どちらのプレイヤーも(自分の得点 - 相手の得点)を最大化しようとする」
となる
次郎君の場合、 X-Yの最小化
= -Y + X の最小化
= - (Y - X) の最小化
= (Y - X)の最大化
= (自分の得点 - 相手の得点)の最大化
そうするとどちらのプレイヤーも同じ操作をすることになり、再帰的に簡潔に書けるようになる。
func calculate(l, r int) int {
// memoにあったらそれを返す
if memo[l][r] {
return dp[l][r]
}
memo[l][r] = true
l, rが同じ = 最後の一つ
if l == r {
dp[l][r] = sli[l]
return sli[l]
}
// 左右それぞれを取った場合を計算
l_score := calculate(l+1, r)
r_score := calculate(l, r-1)
// それぞれを比較し、大きいほうを返す
dp[l][r] = Max(sli[l]-l_score, sli[r]-r_score)
return dp[l][r]
}
コード(AC)
func run() interface{} {
n := readInt()
sli = readSli(n)
dp = make([][]int, n+1)
memo = make([][]bool, n+1)
for i := 0; i <= n; i++ {
dp[i] = make([]int, n+1)
memo[i] = make([]bool, n+1)
}
// l, rのインデックスを添字もつDPを考える
ans := calculate(0, n-1)
return ans
}
// return sum of score
func calculate(l, r int) int {
if memo[l][r] {
return dp[l][r]
}
memo[l][r] = true
if l == r {
dp[l][r] = sli[l]
return sli[l]
}
l_score := calculate(l+1, r)
r_score := calculate(l, r-1)
dp[l][r] = Max(sli[l]-l_score, sli[r]-r_score)
return dp[l][r]
}
この記事が気に入ったらサポートをしてみませんか?