SoundHound Inc. Programming Contest 2018 -Masters Tournament-

SoundHound Inc. Programming Contest 2018 -Masters Tournament- - AtCoder

リアルタイム参加できませんでしたが問題を解きました.

自分のためだけに記録として書きます.

A問題

A - F

むちゃくちゃ素直にif文を書いて判定する.

B問題

B - Acrostic

文字列Sを受け取って,1文字目,w+1文字目,2w+1文字目…と表示していけばいい!これを(文字列の長さ)文字目まで繰り返せばいい!

まあいけるでしょB問題だから,と思ったらまさかの1WAしました…for文の終了条件を,(文字列の長さ)÷w以下にしたことによるミス.文字列の長さがwの倍数なとき,受け取った文字列より長いところにアクセスして出力してしまいますがな…(あほ)

割り算をやめて,wずつ加算してくようにして無事通りました.B問題でミスるとメンタルに来る.

C問題

C - Ordinary Beauty

30分考えたけどわからなかった!!!!!!!!!!

開き直って解説を読むも,期待値の線形性??なにそれ????状態だったので,ツイッターでより噛み砕いた解説を探したり,以下の記事を読んだり(この方のQiitaの記事いつもめちゃくちゃ勉強になってありがたいです)

qiita.com

結局,競技プログラミングにおける確率の知識がないという結論に至って周辺知識を勉強することにしました.

解説見た上で,自分で解いておきました.

D問題以降

こんな感じなのでD問題以上は厳しいと考え見てすらいない…D問題をこなせる日が来るよう,またコツコツとやっていきます.

ABC 068 C問題 Cat Snuke and a Voyage

問題

C - Cat Snuke and a Voyage

考察の流れ

高橋キングダム島ありすぎワロタ (3 \leq N \leq 2×105

グラフを使ってあれやこれやする感じなのかな…?と思いかけたけどよくよく見ると島1から島Nまで,どこか一つの島Xを経由していけるなら構わないということになる.

愚直にやろうとすると島1から定期便で行ける島全てに対して,さらにその島から行ける島全てを見て,その中に島Nがあるか調べればいい.これはO(N2)で,島多すぎワロタなので間に合わない.

計算量を落とす方法を考えなくてはいけなくなった…が,前記事ABC 073 C問題 Write and Erase - おふとん王国でsetを復習してたこともあり,setを使えないかと考える.

入力を受け取るときに,島1,島Nへの定期便がある島をそれぞれsetで持っておいて,各setに共通要素がないか見ていけばいい.

島1に行ける島のsetから全部要素を取り出し(O(N)),その要素が島Nに行ける島のsetにあるか調べる(O(log N))ようにすれば,計算量はO(N log N)で間に合わせることができる.

コード

Submission #2799428 - AtCoder Beginner Contest 068

1発AC 所要時間20minくらい

反省

  • 解説を見たところ…なるほど,辺の情報をハッシュマップに入れてしまえばO(1)で行けますね やはりデータ構造は弱いので,復習しておこう…
  • とはいえ確実に通せる解法で通せたのは良かった.
  • 今回はグラフのアルゴリズムは使わなくてもOKだったけど,せっかくなのでグラフの復習もしておく.

ABC 073 C問題 Write and Erase

問題

C - Write and Erase

joisinoお姉ちゃんってなんでjoisinoなんでしょ?

考察の流れ

joisinoお姉ちゃんが言った数について,言った回数が奇数回なら最終的に紙に書かれているし,偶数回なら紙に書かれていないことになる.

シンプルなのは各数(1 \leq A_i \leq 109)が最終的に書かれているかの情報を持たせた配列を使って,その個数を求める方法だけど,109回ループを回すと間に合わない.

そこで,受け取った数列をソートして(O(N log N)),各数の個数を前から数えていき(O(N)),奇数の場合は結果としてカウントすることにする.

これなら計算量的に間に合う.

コード

Submission #2760411 - AtCoder Beginner Contest 073

1発AC 所要時間20minくらい

反省

  • ABC 071 C問題 Make a Rectangle - おふとん王国のコードと同じで,各数の個数を前から数えていくコードがシンプルじゃない感じになっています.解説に書いてある実装を復習する.
  • シンプル解で,配列ではなくsetとかに情報を持たせれば,計算量的に間に合うということもわかってはいましたが,setをそんなに使ったことがなかったのでやめました.蟻本を読み返す.

ABC 072 C問題 Together

問題

C - Together

考察の流れ

ある Xを選んだとき, a_i =Xとなる iの個数を最大化する.

まず, Xを固定した場合. a_i =Xとなる iの個数を最大化するには,数列中の全ての X-1に+1,全ての X+1に-1してあげればいい.

そして, Xとして何を選べば a_i =Xとなる iの個数を最大化できるかについては, Xとして考えられる値を全て調べてあげればいい.各Xに対し,数列中の X-1XX+1の個数の和を出してあげてそれが最大となるXを探す.数列中の X-1XX+1の個数の和を出す処理は,数列をあらかじめソートしておいて二分探索することでO(log N)で可能となる.

また, Xとして考えるべきなのは,数列の値の範囲内のみで問題ない(0\leqX<105).

以上より全体としての計算量はO(105 log N)なので間に合わせることができる.

コード

Submission #2760497 - AtCoder Beginner Contest 072

1発AC 20 minくらいで解いた.

反省

  • カッコよく二分探索をキメたつもりでいたけど, Xを列挙するループ内で毎回二分探索をする必要はない(最初に各数の個数を調べておけばよかった).これで計算量を落とすことができる.
  • とはいえ確実に行けるかつ間に合うやり方で通せたのは良かった.

ABC 069 C問題 4-adjacent

なるべく一日一題を目標に…(三日坊主になってしまいそうですが)

問題

C - 4-adjacent

すぬけくん登場!

考察の流れ

となりあった要素をかけたら全部4の倍数になるようにうまく並べ替える方法があるか?

数列の各数を「奇数」「2の倍数だが4の倍数でない」「4の倍数」の3つに分類したとき,奇数と隣り合う数は絶対4の倍数でないとだめであるため,それらを前から詰めていくことを考える.

奇数がある場合

  • (奇数の個数)+1 <(4の倍数の個数)なら,前から奇数と4の倍数を交互につめればいい.
  • (奇数の個数)+1 = (4の倍数の個数)ときうまく並び替えられるのは (奇数の個数)+(4の倍数の個数)=(数列のサイズ)になる場合.もし,(奇数の個数)+(4の倍数の個数)<(数列のサイズ)になる場合は,奇数と4の倍数を交互に詰めていったとき,「奇数」「2の倍数だが4の倍数でない」という並びができてしまい,実現不可能.

奇数がない場合

全て偶数なため,どんな並べ方をしても,隣り合う要素をかければ4の倍数となる

というわけで,「奇数」と「4の倍数」の個数を入力受取時にカウントすれば,ループを回さなくてもO(1)で解くことができる!

コード

Submission #2794671 - AtCoder Beginner Contest 069

1発AC 所要時間20minくらい

反省

  • Cなのに400点問題なので解けるか不安でしたがなんとか1発ACをいただきました.O(1)解にたどり着くことができてよかった.
  • 場合分けのところで本当にこれで網羅できてるのかな?と混乱してしまいました.サンプルのテストケースを見てパターンを導き出した感じですが,場合分けの力は磨いていきたいところです.特に解説を読むとよりシンプルな場合分けをしています.

ABC 071 C問題 Make a Rectangle

かかった時間:30min 1発AC

考察の過程

面積を大きくしたいので長い棒から貪欲にとっていけばいい.

ただ長方形でなければいけないので,同じ長さの棒が2本以上あるところからとらなければならない.

また,4本以上同じ長さのものがあったらそこから4本とって正方形にしてしまえばいい.

ソートして長い方から見ていけばいいので,計算量はO(N logN)となる.

コード

https://beta.atcoder.jp/contests/abc071/submissions/2760584

反省

  • 同じ長さの棒の本数を数える→長方形の辺にするかの判定をするところが3分岐していてややこしい感じになってしまっている.
  • 他の人のコードなどを見ると,すべての棒を見ていき,同じ長さの棒が2本あったら1セットとしてリストなどに放り込む→全て放り込んだ上で,それを長い方から見ていって2セットとったのが答え,としている.こういうシンプルな考え方ができるようにならなければいけない.アルゴリズムがややこしくて間違えそう,なときはシンプルに考えられないか?を常に問いかけること(自戒).

ABC 070 C問題 Multiple Clocks

競技プログラミングの問題解いた振り返りを残していくといいとどこかで聞いたので,恥さらしを始めてみます.

かかった時間:25min 1発AC

考察の過程

T_i (1≦i≦N) の最小公倍数が答えになる.

愚直解としては,答えは1018以下であることがわかっているので1以上1018以下の範囲の整数をすべて試して,それらがすべてのT_i (1≦i≦N)で割り切れるか判定すればいいけど,O(1018 × 100)で間に合わない.

では,最小公倍数を求められるもっと計算量の小さいアルゴリズムはないか?約数問題で軽いアルゴリズムといえばユークリッドの互除法(整数aとbの最大公約数の計算量はO(log max(a,b)) )→最大公約数を使って最小公倍数を求めていけないか?

→lcm(i)をT_1〜T_iの最小公倍数とする.lcm(i+1)=lcm(i)×T_i / (lcm(i)とT_iの最大公約数) なのでiを1〜Nまで動かしていけば,計算量がO(N log(max(T_i)))

という感じで考えました.

コード

Submission #2791105 - AtCoder Beginner Contest 070

反省

  • 計算量から解法を考えられるようになったのは成長した.
  • ユークリッドの互除法を思い出すことができた.
  • はじめ,lcm(i+1)=lcm(i)×T_i / (lcm(i)とT_iの最大公約数)の計算で桁あふれすることを考慮してなくておかしな結果になってなんでだろ〜?となり時間がかかってしまった…どうも,大きな数同士の計算の桁あふれに鈍感な傾向にあるので,今後の気をつけるポイント.
  • コードを見返すとlcm(i)とT_i のmax,minを取るという無駄な処理をしている…
  • とはいえ少し前までは解法すら思い浮かばなかったと思うのでこれでも進歩しているのです.