[学習記録]AtCoder EDP N-Slimes[Go]
問題
N 匹のスライムが横一列に並んでいます。 最初、左から i 番目のスライムの大きさは ai です。
太郎君は、すべてのスライムを合体させて 1 匹のスライムにしようとしています。 スライムが 1 匹になるまで、太郎君は次の操作を繰り返し行います。
左右に隣り合う 2 匹のスライムを選び、それらを合体させて新しい 1 匹のスライムにする。 合体前の 2 匹のスライムの大きさを x および y とすると、合体後のスライムの大きさは x+y となる。 このとき、太郎君は x+y のコストを支払う。 なお、合体の前後でスライムたちの位置関係は変わらない。
太郎君が支払うコストの総和の最小値を求めてください。
解法
払ったコストの最小をDPに保存しておけば計算が可能そう。
常に隣り合ったスライムを合体させるので、状態は left, rightで持つことができそう。
入力例1のとき、最後の1回の合体を考えると、
それまで払ったコスト + 合体後の大きさ となり、
それまで払ったコスト
= 10 + [ 20 + 30 + 40 ]
or [ 10 + 20 ] + [ 30 + 40 ]
or [ 10 + 20 + 30 ] + 40
の最小値
合体後の大きさ
= 1 番目から n 番目までの大きさを足した値
となる。
再帰的に計算していけば答えが求められそう。
n := readInt()
sli = readSli(n)
dp = make([][]int, n)
memo = make([][]bool, n) // 再帰なのでメモを取っておく
for i := 0; i < n; i++ {
dp[i] = make([]int, n)
memo[i] = make([]bool, n)
}
ans := calculate(0, n-1)
return ans
それまで払ったコストは、与えられたスライムの位置の内、
どこで分割するかを全て計算し、その最少を取ることで計算できる。
left と right が同じときは合成しない → コスト = 0になる
func calculate(l, r int) int {
if memo[l][r] {
return dp[l][r]
}
memo[l][r] = true
if l == r {
return 0
}
min := max_int64
// i を変化させることで分割位置を変化させている
// left: l ~ i まで、 right: i+1 ~ rまで
for i := l; i < r; i++ {
min = Min(min, calculate(l, i)+calculate(i+1, r))
}
}
合体後の大きさは、 left から right まで足せばいいが、それを毎回やっていると時間が足りなくなるので累積和を用い る。
あとは計算結果としてDPを返せば、答えが得られる
func run() interface{} {
// ...
cum = make([]int, n+1)
for i := 1; i <= n; i++ {
cum[i] = cum[i-1] + sli[i-1]
}
ans := calculate(0, n-1)
return ans
}
func calculate(l, r int) int {
// ...
min := max_int64
for i := l; i < r; i++ {
min = Min(min, calculate(l, i)+calculate(i+1, r))
}
// それまで払ったコスト + 合体後の大きさ
dp[l][r] = min + cum[r+1] - cum[l]
return dp[l][r]
}
コード(AC)
func run() interface{} {
n := readInt()
sli = readSli(n)
dp = make([][]int, n)
memo = make([][]bool, n)
for i := 0; i < n; i++ {
dp[i] = make([]int, n)
memo[i] = make([]bool, n)
}
cum = make([]int, n+1)
for i := 1; i <= n; i++ {
cum[i] = cum[i-1] + sli[i-1]
}
ans := calculate(0, n-1)
return ans
}
func calculate(l, r int) int {
if memo[l][r] {
return dp[l][r]
}
memo[l][r] = true
if l == r {
return 0
}
min := max_int64
for i := l; i < r; i++ {
min = Min(min, calculate(l, i)+calculate(i+1, r))
}
dp[l][r] = min + cum[r+1] - cum[l]
return dp[l][r]
}
この記事が気に入ったらサポートをしてみませんか?