AtCoder NoviStepsを埋めてみる(1) 貪欲法 3Q以下の続きです。ちょっと難しめの問題を考えます。

082 – Interval Scheduling Problem

082 – Interval Scheduling Problem

問題の概要は以下のとおりです。

N 本の映画が上映されます。i 番目の映画は時刻 L[i] に始まり R[i] に終わります。
最大いくつの映画を最初から最後まで見ることができるか?

これは区間スケジューリング問題の典型問題です。

どうしても映画の開始時刻に目がいってしまいますが、ここは終了時刻に着目します。現在時刻からみてもっとも早く終わる映画を選択します。映画が終わったらその時刻からあとに始まる映画でもっとも終了時刻が早いものを選択するという処理を繰り返します。

079 – Two by Two(★3)

079 – Two by Two(★3)

問題の概要は以下のとおりです。

H×W の 二次元配列 A が与えられる。
A[x, y], A[x + 1, y], A[x, y + 1], A[x + 1, y + 1]の値を 1 増やすか減らすかの処理を繰り返すことで、A を B に一致させることは可能か? 可能ならば最小の操作回数は?

A[0, 0]からみていきA[x, y]とB[x, y]に違いがあれば同じになるようにA[x, y], A[x + 1, y], A[x, y + 1], A[x + 1, y + 1]の値を同時に更新します。この方法で B と一致するならその回数を出力します。この方法で一致させることができないならどうやっても一致させることはできないので”No”を出力します。オーバーフローに注意。

D – Take ABC 2

D – Take ABC 2

問題の概要

A, B, C の 3 種類の文字のみからなる文字列 S が与えられる。
S[i] = A, S[j] = B, S[k] = C(ただし i < j < k) を満たす(i, j, k) の組を選び、この文字を取り除く。
残った文字を元の順序を保ったまま左に詰める。
この処理は最大何回繰り返すことができるだろうか?

(i, j, k) の組は大小関係のみを満たしていればよく、連続している必要はありません。なので、最初に見つけた’A’、そのあとにある最初に見つけた’B’、さらにそのあとにある最初に見つけた’C’を削除するという処理を繰り返せばよいです。

問題はどうやってこの処理をおこなうかです。まず文字 ‘A’,’B’,’C’のインデックスをQueueに格納します(qA, qB, qC)。そして’B’のインデックス(idx_b)を取り出します。もしqAに格納されている最小値が idx_b よりも大きければこの idx_b は使いようがありません。なのでこれは捨ててしまって次の idx_b について調べます。

また qC に格納されている idx のなかで idx_b より小さいものも使えません。idx_b はループを繰り返すたびに大きくなっていくので置いておいても使い道はないので qC.Peek() < idx_b である限り qC に格納されている値は捨ててしまいます。

こうして捨てられずに残った qA と qC の先頭の要素と idx_b が S[i] = A, S[j] = B, S[k] = C を満たす(i, j, k) の組です。これを数えればよいです。途中で Queue が空になったら終了です。

C – AtCoder Riko

C – AtCoder Riko

問題の概要

長さ L の棒が何本かある。そのうちいくつか(0本や全部の場合もありうる)が2つに折れてしまった。
これによって棒は全部で N 本になり、i 本目の棒の長さは A[i]である。
L としてありうる長さをすべて求めよ(ただし必ず解がひとつ以上存在する入力しか与えられない)。

考えられるパターンとして、(1)棒はすべて折れてしまった。(2)折れずにそのままの棒が存在する のふたつがあります。

すべて折れてしまったのであれば棒の総数は偶数であり、一番長い棒と一番短い棒、二番目に長い棒と二番目に短い棒・・・の長さの合計が同じ値になるはずです。

折れずにそのままの棒が存在するのであれば、その棒はもっとも長い棒です。この棒を取り除いて(1)の場合と同じようなことをすれば長さの合計は一番長い棒の長さに一致するはずです。

(1)と(2)について矛盾しない値が存在するかを調べれば解が得られます。

C – Alternated

C – Alternated

問題の概要

文字 ‘A’, ‘B’ を N 個ずつ含む長さ 2N の文字列 S が与えられる。
S に対して隣り合う文字を入れ替える操作をおこなって同じ文字が隣り合う箇所がない状態にしたい。
操作回数の最小値を求めよ。

同じ文字が隣り合う箇所がない状態にするには “ABABAB…” とするか “BABABA…” とするしかありません。

‘A’の位置に着目して S のなかにある’A’の位置をリストに格納します。また “ABABAB…” としたときの’A’の位置と “BABABA…” としたときの’A’の位置も取得しておきます。あとは各要素の値の差の絶対値の総和を調べます。これが目標とする文字列に変えるために必要な隣り合う文字を入れ替える回数となります。

D – Get Many Stickers

D – Get Many Stickers

問題の概要は以下のとおり。

最初、中身が入ったコーラが N 本ある。
空き瓶を中身が入ったコーラに交換してくれる店が M 箇所にある。i 番目の店では A[i] 本の空き瓶と引き換えに B[i] 本のコーラと記念のシール 1 枚をもらうことができる。
シールをできるだけたくさん集めたい。最大で何枚集めることができるだろうか?

シールをたくさんもらいたいのであれば空き瓶を交換するときに所持する瓶の総数ができるだけ減らないようにすべきです。そこで交換する店は A[i] – B[i] が小さく、A[i]が現在所持する空き瓶以下のものを選び続けるべきです。

N が大きいので交換するたびに N から A[i] – B[i] を引き続けては実行制限時間以内に処理が終わりません。割り算をしてまとめて処理をしたいのですが、N から A[i] を引いた段階で N が負数にならないような交換方法を採用する必要があります。

そのために N から A[i]だけ引いたものを割り算してまとめて交換処理をしています。ただこの方法だと1本も交換できない場合があるのでその場合は普通に1回分ずつ交換の処理をしています。

C – Giant Domino

C – Giant Domino

問題の概要

1 から N までの番号がついた N 個のドミノがある。ドミノ i の大きさは S[i] である。
ドミノが右に向けて倒れる時、そのすぐ右に置かれているドミノの大きさがその 2倍 以下であればそれも右に倒れる。
一番左に 1 番目のドミノ、一番右に N 番目のドミノを配置してすべてのドミノが倒れるようにしたい。
そのために必要なドミノの最小個数は何個だろうか?

左側においたドミノの2倍以下のもので、最大または N 番目のドミノを右に配置していけばよいです。