penguin46の精進記録

気まぐれで何かを書きます

Educational Codeforces Round 99(Div.2) D. Sequence and Swaps 解説

初投稿なので丁寧目に書きました。たぶん今後は徐々にテキトーになります(苦笑)

問題リンク

codeforces.com

 

問題概要

 n個の非負整数からなる数列 a=\{a_1, a_2, ..., a_n\}と、非負整数 xが与えられます。次の操作を好きな回数行えます。

   操作:  xより大きい aの要素 a_iを選択し、 a_i xを入れ替える。

 数列 aを広義単調増加にするために必要な操作回数の最小値を求めてください。ただし、不可能な場合は -1と出力してください。

 

制約

 1 \leq t \leq 500,  1 \leq n \leq 500,  0 \leq x \leq 500

ただし、 nの和は500を超えない。 tはクエリの個数。

 

解法

 xと交換する aの要素は必ず xより大きいので、スワップを繰り返すにつれて xは(狭義)単調増加することと、ソート後の数列は左に行くほど小さくなることを考えると、直観的に xスワップできる要素の内、最も左の要素をスワップをしていきたくなります。証明します。

 証明

  x(=x') a_i, a_j (i \lt j)スワップできるとします。この時 a_i, a_j \gt xが成り立ちます。ここで a_jスワップした場合、数列の並びは a_i, x'となり、 x=a_jです。 a_i \gt x'でしたから、この2要素の並びはこのままではいけません。しかし、さらに x(=a_j) a_iスワップしても a_j \gt x'なので大小関係が逆のままとなります。1回目あるいは2回目のスワップの直後に他から要素を貰ってくることもできますが、 xは単調増加であり、 x'よりも小さな xを貰うことはできないため、 i番目の位置には x'以下の要素を置くことができません。よって、2つ以上スワップできる要素があった時、そのうち最も左の要素とスワップするべきであり、逆にその他の要素とスワップするとソートが完遂しません。従ってこれが唯一の手順となり、他の手順が存在しないため、当然最小の手順にもなります。

 

 ただし、途中でスワップできたとしても、ソートが完了していればスワップする意味はありませんので、その時点で終了すべきです。(←自戒)

 実装

  1. 数列がソート済みであるかを確認する。ソート済みなら終了。
  2. 数列を前から見ていき、スワップできるならばスワップする。
  3. 最後に数列を確認し、ソートされていれば回数を、ソートされていなければ-1を出力する。

 

codeforces.com 

感想

 コンテスト中はソート済みかの確認を忘れて1ペナを出してしまいました。今回のコンテストは(少なくともDまでは)、解法がわかってしまえば実装は楽な問題が多かった気がします。あと、解説記事を書くとコンテスト中は適当になりがちな証明を詰めれていいですね。続けていきたいです。