AGC032-D:Rotation Sort

やっぱりAGCの問題は綺麗で良いなあ...。

解法

 Rotation操作はややこしいので、言い換えを考える。よく眺めると、右シフトは「好きな数を今の位置より右側の好きな位置に移す」と言い換えられる。左シフトも同様なので、結局

  1. 「コストAを払い、好きな数を今の位置より右側の好きな位置に移す」
  2. 「コストBを払い、好きな数を今の位置より左側の好きな位置に移す」

とい2つの操作を考えればよい。この操作ができるとき、ソートに必要なコストの最小値を求めることが求められている。

 さて、sortするのに必要な最小コスト(または操作回数)を求める場合、dpを考えると有効な場合がある。特に、小さい数字からどの(相対的な)位置に置くか?ということを決めていくdpを考えるとうまくいきそうな気がしてくる。例えば、1\sim i-1までは昇順に並んでいるとすれば、iをどの位置に置くべきかはi-1との位置関係だけを見れば十分になるというのは都合が良いだろう。従って、dpの状態として「1からiまでは昇順に並んでいる」ということを持つことにする。もちろんこれだけでは不十分である。なぜ不十分かというと、iより大きい数も含めたときに、全体としてどれくらい揃っているかということも重要だからである。ここでどういう情報を持つとよいかは難しいが、筆者は「iより手前の位置にあって、iより大きい数がj個ある」という情報を持った。まとめると

  • dp_{i,j}:1からiまでの数は昇順に並んでいて、iより左側にあってiより大きい数がj個あるような順列にするときの、操作コストの最小値」

としてdpを行うことにした。
 遷移を考えよう。まず、各数に行う操作は「左に動かす」「右に動かす」「動かさない」でいずれかを1回のみでよいのは明らかだろう。さらに、dpの遷移の際にはi-1までは昇順に並んでいるとしているので、以下のように考えてよい。

  1. i-1iより左側にあるときにiに行う操作は「左に動かす」か「動かさない」
  2. i-1iより右側にあるときにiに行う操作は「右に動かす」

つまり、i-1iの位置関係が管理できれば遷移が可能になる。そこで、l_i:元の順列\{p_i\}に関して、iより左側にありiより大きい数の個数 とする。すると、i-1からiへの遷移について、j \leq l_iのときは1に該当し、そうでないときは2に該当することがわかる。よって、以下のような遷移式が書ける。

  1. dp_{i,j} = \min(dp_{i,j},dp_{i-1,j}+B) (1 \leq j \leq l_i)
  2. dp_{i,l_i} = \min(dp_{i,l_i},dp_{i-1,j}) (1 \leq j \leq l_i)
  3. dp_{i,j-1} = \min(dp_{i,j-1},dp_{i-1,j}+A) (l_i \lt j \leq N)

初期値はdp_{0.0} = 0で他は\infty、答えはdp_{N,0}となる。

実装

Submission #6232045 - AtCoder Grand Contest 032
実装はこの問題に取り組むレベルの人なら特に難しいところはないはず。