あるエンジニアのAtCoder奮闘記

東京都港区にあるアミフィアブル株式会社のエンジニアが、AtCoderで解いた問題について振り返ったりしていく会社公認のブログです。

AtCoder diverta 2019 Programming Contest 2 C - Successive Subtraction

irisruneです。他言語に慣れているとpythonの変数名にキャメルケースを使おうとするたびに警告を出されるのはもやもやします。

atcoder.jp

B問題より簡単という噂を聞きましたが全然そんなことはなかったです。見た目以上に考えることが多く、ACするのに5回かかりました。

import sys
import collections as col
import bisect
import numpy as np


def main():
    n = int(input())
    aarray = [int(e) for e in input().split()]
    aarray.sort()
    record = []
    endpoint = max(1, bisect.bisect(aarray, 0))
    aqueue = col.deque(aarray)
    while len(aqueue) > endpoint + 1:
        amax = aqueue.pop()
        amin = aqueue.popleft()
        record.append((amin, amax))
        aqueue.appendleft(amin - amax)
    amax = aqueue.pop()
    while len(aqueue) > 0:
        amin = aqueue.popleft()
        record.append((amax, amin))
        amax -= amin
    print(amax)
    for r in record:
        print(r[0], r[1])


main()

x'-(y'-z')=(x'-y')+z'という性質に気付くかどうかが3割くらいだと思います。アルゴリズムが組めるかが4割くらい、コーナーケースの考慮と実装が3割くらいじゃないでしょうか。

上記の性質を使うことで、y'=A_i\leq0,z'=A_j\gt0となるように操作を行えば解くことができます。…と言いたいところですが、すべてのiについてA_i\gt0となる場合を考える必要があります。なぜかといえば、y'=A_iとなるA_iが最低1つは必要となるためです。

また、逆にすべてのiについてA_i\lt0となる場合についても考える必要があります。こちらについても、x'=A_iとなるA_iが必要となるためです。

以上より、具体的なアルゴリズムは以下のようになります。

  1. A_iを昇順にソートする。
  2. A_i\leq 0 \lt A_{i+1}となるiを探索する(線形探索でも二分探索でも可)。
  3. x=A_1,y=A_j(j\gt i+1)とおいて操作を行い、その結果をA_1に代入することを繰り返す。
  4. x=A_{i+1},y=A_j(j\lt i+1)とおいて操作を行い、その結果をA_{i+1}に代入することを繰り返す。
  5. A_{i+1}が答えとなる。

…とここまで書いていて気が付いたのですが、単にx'=\max A_iとなるようにおけばよいだけでした。無駄に複雑な処理を行っていましたね。

ちなみに実装については\max A_i,\min A_iの両方を繰り返し用いるためdequeを使うとスマートに実装できると思います。

雑記

  • D問題も見た感じは解けるかと思いましたが、こちらも見た目よりかなり考えることが多く自力ACは難しそうです。