🐸

[EDPC C] Vacation

2024/04/01に公開

https://atcoder.jp/contests/dp/tasks/dp_c

難しめのDP問題。

例1で問題を理解する

1〜3日目までに A B C のどの活動をしたら幸福度が最大になるか?

A B C
1日目 10 40 70
2日目 20 50 80
3日目 30 60 90

これはひと目 C → C → C の 70 + 80 + 90 = 240 がもっとも幸福度が高くなるように見えるが、連続で同じ活動をしちゃいけないので、二日目はしかたなく B を選んで、C → B → C の 70 + 50 + 90 = 210 が答えになる。

解説を元にした実装 (AC)

228 ms
N = gets.to_i                                         # => 3
ABC = N.times.collect { gets.split.collect(&:to_i) }  # => [[10, 40, 70], [20, 50, 80], [30, 60, 90]]
W = ABC.first.size                                    # => 3
dp = Array.new(N.next) { Array.new(W, 0) }            # => [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
(1..N).each do |y|
  W.times do |x|
    W.times do |i|
      if i == x
        next
      end
      if dp[y][x] < dp[y - 1][i] + ABC[y - 1][x]
        dp[y][x] = dp[y - 1][i] + ABC[y - 1][x]
      end
    end
  end
end
p dp[N].max                                           # => 210
A B C
0 0 0 0
1 10 40 70
2 90 120 120
3 150 180 210

たとえば dp[2] の行では ABC は 20 50 80 なので、一つ上の 10 40 70 からそれぞれを足すと、

10 40 70
20 10 + 20 = 30 40 + 20 = 60 70 + 20 = 90
50 10 + 50 = 60 40 + 50 = 90 70 + 50 = 120
80 10 + 80 = 90 40 + 80 = 120 70 + 80 = 150

となるが、連続で同じ活動ではできない (たとえば dp[2][A] は同じ ABC の A が使えない) ので、

10 40 70
20 40 + 20 = 60 70 + 20 = 90
50 10 + 50 = 60 70 + 50 = 120
80 10 + 80 = 90 40 + 80 = 120

クロスした部分の計算は使えず、いいとこどりで 90 120 120 になる、ということか。書いてて頭がこんがらがってくる。

リファクタリング (AC)

W.times do |i|
  W.times do |j|
    i == j and next
    [i, j]  # => [0, 1], [0, 2], [1, 0], [1, 2], [2, 0], [2, 1]
  end
end

は、

W.times.to_a.permutation(2).to_a  # => [[0, 1], [0, 2], [1, 0], [1, 2], [2, 0], [2, 1]]

と書けるので、

226 ms
N = gets.to_i                                         # => 3
ABC = N.times.collect { gets.split.collect(&:to_i) }  # => [[10, 40, 70], [20, 50, 80], [30, 60, 90]]
W = ABC.first.size                                    # => 3
dp = Array.new(N.next) { Array.new(W, 0) }            # => [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
WA = W.times.to_a                                     # => [0, 1, 2]
(1..N).each do |y|
  WA.permutation(2) do |x, i|
    dp[y][x] = [dp[y][x], dp[y - 1][i] + ABC[y - 1][x]].max
  end
end
p dp[N].max                                           # => 210

でもいい。が、冗長に書いた方がわかりやすい気もする。

参考

Discussion