−目次
AtCoder Beginner Contest 103
A - Task Scheduling Problem
問題
- 3個のタスクがあり、それぞれに値 A1,A2,A3 が決められている
- 最初のタスクはコスト0で完了できる
- 2番目以降の完了には、直前のタスクとの Ai の差の絶対値のコストがかかる
- 全てのタスクを完了させるためのコストの最小値は?
解法
選ぶタスクの Ai を増やしたり減らしたり行ったり来たりすると、往復する分が無駄なコストとしてかかってしまうので、小さい方から徐々に増やす(または大きい方から減らす)のがよい。
間を省略すると、A1,A2,A3 の最大値ー最小値が答えとなる。
1 2 |
a, b, c = map(int, input().split())print(max(a, b, c) - min(a, b, c)) |
B - String Rotation
問題
- 文字列 S,T がある
- 文字列 S を「回転させる」とは、S の末尾の文字を先頭に持ってくる操作を指す
- ABCDE → EABCD
- 文字列 S を回転させて、T に一致させられるか調べよ
解法
全部試す。
1 2 3 4 5 6 7 8 9 |
s = input()t = input()for _ in range(len(s)): if s == t: print('Yes') break s = s[-1] + s[:-1]else: print('No') |
C - Modulo Summation
問題
- 正整数列 {a1,a2,...,aN} がある
- 1つの正整数 m に対して、f(m)=(mmoda1)+(mmoda2)+...+(mmodaN) とする
- XmodY は、XをYで割った余り
- 上手く m を選んで f が取れる最大値を求めよ
解法
ある ai で正整数を割った余りは 0~ai−1 の間である。つまり最大は ai−1 である。
全ての ai について最大値 ai−1 を取れるような m が存在すれば、f の最大値は (a1−1)+(a2−1)+...+(aN−1) となる。
じゃあ、そんな m は存在するのか。
最大値 ai−1 を得られるのは、ai の倍数から1少ない数 m=kai−1 の時である。
m′=a1×a2×...×aN のように全ての ai をかけてしまえば、m′ は a1~aN 全ての数の倍数である。
よって、m=m′−1 とすることで、全ての ai に対して、最大値の ai−1 が得られる。
m を実際に計算する必要は無い。
1 2 3 4 5 6 |
n = int(input())aaa = list(map(int, input().split()))ans = 0for a in aaa: ans += a - 1print(ans) |
D - Islands War
問題
- 東西1列に並んだ N 個の島がある
- N−1 本の橋によって、隣同士の島が結ばれている
- いくつかの島で戦争が起こり、以下のような要望が M 個寄せられた
- 島 a と b を、その間にあるいずれかの橋を壊して、移動できないようにしてほしい
- 全ての要望を叶えるために、壊す橋の最小数はいくつか?
例
1 2 3 4 ○===○===○===○ > 1と3を移動できないように > 2と4を移動できないように →2と3の間にある橋を壊せば、2つの要望を叶えられる 答えは1本
解法
必要が生じるまで橋は壊さないことにして、西から順に見ていく。
ある要望 ai,bi について、bi まで見た時に、ai 以降のどの橋も壊されてなければ、bi−1 と bi の間の橋を壊す必要がある。
ai bi
○=○=○=○=○=○=○=○
↑ココマデミタ
ai bi
○=○=○=○ ○=○=○=○
↑壊す必要がある
他の要望のために既に ai 以降のいずれかの橋が壊されていれば、何もしなくてよい。
ai bi ○=○=○ ○=○=○=○=○
壊す時、ai~bi の間ならどの橋でもいいのだが、出来るだけ右の橋を壊した方が、他の要望の区間を多く含めるようになる。
a b
○=○=○=○=○=○=○=○
~~~~~~~~
~~~~~~~~~~~~~~~~
~~~~~~~~~~~~
↑ ↑
① ②
bi の小さい方から見ていくので、①を含み、右端がbより右にある区間は、必ず②も含んでいる。
実装
まず各要望について「am と要望番号 m」「bm と要望番号 m」の計 2M 個の情報をリスト化し、ソートする。
ソート時、同じ島なら b の方が a より前に来るようにする。(b1 と a2 が同じ島である2つの区間は、共有する橋を持たないため)
a1 b1
○=○=○=○=○=○=○=○
a2 b2
「保留中リスト」と「確定済みリスト」を用意し、上記のソート済みリストを順に見ていく。
ai が出てきたら、要望番号 i を保留中リストに蓄積する。
bi が出てきたら、
- 要望番号 i がまだ叶えられてなければ、(確定済みリストに無ければ)
- 橋を1つ壊す
- 保留中リストの要望を全て確定済みリストに移す
- 保留中リストを空にする
- 要望番号 i が既に叶えられていれば
- 何もしない
以上で、壊した橋の数が答えとなる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
n, m = map(int, input().split())ab_list = []for i in range(m): a, b = map(int, input().split()) ab_list.append((a - 1, 2, i)) ab_list.append((b - 1, 1, i))ab_list.sort()ans = 0separated = [False] * mbuf = set()for island, ab, i in ab_list: if ab == 1: if separated[i]: continue else: ans += 1 for j in buf: separated[j] = True buf.clear() else: buf.add(i)print(ans) |
実装2
もっとよい実装が解説されていた。
(b,a) ペアについてソートして、「最後に壊した最も東の橋 r」を記録しながら西から見ていく。
a が r より小さければ、既に要望は叶えられている。
a が r より大きければ要望はまだなので、橋を壊してr=b に更新する。
要望番号を管理しなくても、これで十分カウントできる。
また、b が同じ要望が複数ある場合、必要なのは a が最も東にある1つだけなので、その辺の重複を最初のソートリスト作る時に削除すると、後半のループが減ってなお高速になる。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
n, m = map(int, input().split())disputes = []for i in range(m): a, b = map(int, input().split()) disputes.append((b, a))disputes.sort()ans = 0most_right_bridge = 0for b, a in disputes: if most_right_bridge <= a: ans += 1 most_right_bridge = bprint(ans) |

