🚧 This website is under construction. 🚧
AtCoder
ABC
197
E

ABC
DP

ABC197 E - Traveler

https://atcoder.jp/contests/abc197/tasks/abc197_e (opens in a new tab)
水色下位。DP。

同じ色のついたボールを取得する方法について、端から端まで順番にとっていく方法が最適となる。 その際、左から始めるか右から始めるかで 2 通りの選択肢があるため、ここを DP を用いて高速に計算する。

ここまで落とし込めたら基礎 DP かと思いきや、遷移式の抽象化にかなり苦労した。 まず「左端から右端の遷移」みたいなものを最初考えていたが、実は元の位置は別にどちらでもよい つまり、左 → 右、左 → 左、右 → 左、右 → 左ではなく、x→ 右、x→ 左と抽象化できる x から右端に行くには、左端を経由するべきだし、x から左端に行くには右端を経由すべきだから、結局 3 重ループで簡潔に遷移を表現できることになる

INF = float('inf')
 
n = int(input())
 
# balls[i] := 色iのボールについて、(左端ボールの座標、右端ボールの座標)
balls = {-1: [0, 0]}
 
for _ in range(n):
    x, c = map(int, input().split())
    left, right = balls.get(c - 1, [INF, -INF])
    balls[c - 1] = [min(left, x), max(right, x)]
 
balls = [e for _, e in sorted(balls.items())]
 
# dp[i][j] := 色iのボールをすべて回収した時に(j == 0 ? 左 : 右)側にいる場合の時刻
dp = [[INF, INF] for _ in range(len(balls))]
dp[0] = [0, 0]
 
for i in range(len(balls) - 1):
    for j in range(2):
        x = balls[i][j]
        for k in range(2):
            dp[i + 1][k] = min(
                dp[i + 1][k], dp[i][j] + abs(balls[i + 1][~k] - x) +
                balls[i + 1][1] - balls[i + 1][0])
 
print(min(dp[-1][0] + abs(balls[-1][0]), dp[-1][1] + abs(balls[-1][1])))