−目次
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)=(m \mod{a_1})+(m \mod{a_2})+...+(m\mod{a_N}) とする
- X\mod{Y} は、XをYで割った余り
- 上手く m を選んで f が取れる最大値を求めよ
解法
ある a_i で正整数を割った余りは 0~a_i-1 の間である。つまり最大は a_i-1 である。
全ての a_i について最大値 a_i-1 を取れるような m が存在すれば、f の最大値は (a_1-1)+(a_2-1)+...+(a_N-1) となる。
じゃあ、そんな m は存在するのか。
最大値 a_i-1 を得られるのは、a_i の倍数から1少ない数 m=ka_i-1 の時である。
m'=a_1\times a_2 \times ... \times a_N のように全ての a_i をかけてしまえば、m' は a_1~a_N 全ての数の倍数である。
よって、m=m' - 1 とすることで、全ての a_i に対して、最大値の a_i-1 が得られる。
m を実際に計算する必要は無い。
1 2 3 4 5 6 |
n = int ( input ()) aaa = list ( map ( int , input ().split())) ans = 0 for a in aaa: ans + = a - 1 print (ans) |
D - Islands War
問題
- 東西1列に並んだ N 個の島がある
- N-1 本の橋によって、隣同士の島が結ばれている
- いくつかの島で戦争が起こり、以下のような要望が M 個寄せられた
- 島 a と b を、その間にあるいずれかの橋を壊して、移動できないようにしてほしい
- 全ての要望を叶えるために、壊す橋の最小数はいくつか?
例
1 2 3 4 ○===○===○===○ > 1と3を移動できないように > 2と4を移動できないように →2と3の間にある橋を壊せば、2つの要望を叶えられる 答えは1本
解法
必要が生じるまで橋は壊さないことにして、西から順に見ていく。
ある要望 a_i,b_i について、b_i まで見た時に、a_i 以降のどの橋も壊されてなければ、b_i-1 と b_i の間の橋を壊す必要がある。
ai bi ○=○=○=○=○=○=○=○ ↑ココマデミタ ai bi ○=○=○=○ ○=○=○=○ ↑壊す必要がある
他の要望のために既に a_i 以降のいずれかの橋が壊されていれば、何もしなくてよい。
ai bi ○=○=○ ○=○=○=○=○
壊す時、a_i~b_i の間ならどの橋でもいいのだが、出来るだけ右の橋を壊した方が、他の要望の区間を多く含めるようになる。
a b ○=○=○=○=○=○=○=○ ~~~~~~~~ ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~ ↑ ↑ ① ②
b_i の小さい方から見ていくので、①を含み、右端がbより右にある区間は、必ず②も含んでいる。
実装
まず各要望について「a_m と要望番号 m」「b_m と要望番号 m」の計 2M 個の情報をリスト化し、ソートする。
ソート時、同じ島なら b の方が a より前に来るようにする。(b_1 と a_2 が同じ島である2つの区間は、共有する橋を持たないため)
a1 b1 ○=○=○=○=○=○=○=○ a2 b2
「保留中リスト」と「確定済みリスト」を用意し、上記のソート済みリストを順に見ていく。
a_i が出てきたら、要望番号 i を保留中リストに蓄積する。
b_i が出てきたら、
- 要望番号 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 = 0 separated = [ False ] * m buf = 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 = 0 most_right_bridge = 0 for b, a in disputes: if most_right_bridge < = a: ans + = 1 most_right_bridge = b print (ans) |