問題
点が $N$ 個あたえられます.点 $i$ の高さは $Y_i$ です.Noel ちゃんは点の高さを自由に動かせます.ある日Noelちゃんは,点の高さが広義単調増加になるように点を動かしたくなりました.Noel ちゃんが動かす必要のある距離の総和の最小値を求めてください.ただし Noel ちゃんは,どの点も整数の距離だけしか高さを変えられないものとします.
要するに,与えられた数列を広義単調増加にしてください,という問題.
制約
- $1 \leqq N \leqq 10^{3}$
- $0 \leqq Y_i \leqq 10^{4}$
解法
dp
で解く.配列は,$(N + 1) \times (\max\{Y\} + 1)$ の二次元配列として持つ.また,dp
配列の第 $(i, j)$ 要素は次のように定義される.
dp[i][j]
:数列 $Y_0,Y_1,\dots,Y_{i-1}$ が広義単調増加となるように点の移動が完了しているとき,$Y_i$ を $j$ にしたときに必要なコストの最小値
※ ある数列 $\{A_i\}$ が広義単調増加であるとき,第 $i$ 番目の要素を $x$ としたとき,それ以前の要素($A_0, A_1, \dots, A_{i-1}$)はすべて $x$ 以下である.
したがって,遷移の仕方は次のように表せる.
$$
\mathrm{dp}[i][j] = \min_{0 \leqq k \leqq j} \{ \mathrm{dp}[i-1][k] \} + | Y_i - j |
$$
添字に注意して実装してあげると,以下のようになる.
n = int(input())
Y = list(map(int, input().split()))
mx = max(Y)
INF = 10 ** 18
dp = [[INF] * (mx + 1) for _ in range(n)]
for j in range(mx + 1):
dp[0][j] = abs(Y[0] - j)
for i in range(1, n):
mn = 10 ** 18
for j in range(mx + 1):
mn = min(mn, dp[i - 1][j])
dp[i][j] = mn + abs(Y[i] - j)
print(min(dp[-1]))
例
入力
6
1 2 0 8 6 9
結果(実行後のdp
配列)
for v in dp: print(v)
[1, 0, 1, 2, 3, 4, 5, 6, 7, 8]
[3, 1, 0, 1, 2, 3, 4, 5, 6, 7]
[3, 2, 2, 3, 4, 5, 6, 7, 8, 9]
[11, 9, 8, 7, 6, 5, 4, 3, 2, 3]
[17, 14, 12, 10, 8, 6, 4, 4, 4, 5]
[26, 22, 19, 16, 13, 10, 7, 6, 5, 4]