【AtCoder:1回目】AtCoder Beginner Contest 126の振り返り(Ruby)

f:id:ryoutaku_jo:20190520222917p:plain

【結論】

Rubyで回答しています。

・ABCしか解けなかったので、振り返りはその3問のみです。

・解説ではなく、自身の振り返りです(クソコードしか書いてない・・・)

【目次】

【本題】

AtCoder Beginner Contest 126の振り返り

初めてAtCoderのコンテストに挑戦したので、その振り返しをしたいと思います。

今回挑戦したのは、5/19(日)に開催された「AtCoder Beginner Contest 126」です。

AtCoder Beginner Contest 126 - AtCoder

結果

f:id:ryoutaku_jo:20190520213650p:plain

6問中、3問しか解けませんでした・・・

それに、デバック用に記述していたputsを消し忘れて「不正解」になる等、凡ミスも多発しました・・・

それでも、初めてにしては上出来だと前向きに捉えています!

A - Changing a Character

では、1問目から振り返ります。

問題文 A, B, C からなる長さ N の文字列 S と、1 以上 N 以下の整数 K が与えられます。 文字列 S の K 文字目を小文字に書き換え、新しくでき S を出力してください。

こちらに対する回答が以下の通りです。

N,K = gets.chomp.split(" ").map(&:to_i);
S = gets.chomp
 
result = []
 
S.chars.each_with_index do |str, index|
  if index == K - 1
    result << str.downcase
  else
    result << str
  end
end
 
puts "#{result.join}"

IF文で該当の文字列を特定し、小文字に置き換えるという処理です。

でも、他の方々の回答を見ていると、もっと良いやり方がありました。

n,k=gets.split.map &:to_i
s=gets
s[k-1]=s[k-1].downcase
puts s

文字列って、配列みたいな方法で文字を指定して取得出来るんですね💦

これで、いちいちIF文で、対象の文字列を特定しなくても、小文字に変換できるので、非常にスマートになりました!

B - YYMM or MMYY

問題文 長さ 4 の数字列 S が与えられます。あなたは、この数字列が以下のフォーマットのどちらであるか気になっています。

YYMM フォーマット: 西暦年の下 2 桁と、月を 2 桁で表したもの (例えば 1 月なら 01) をこの順に並べたもの MMYY フォーマット: 月を 2 桁で表したもの (例えば 1 月なら 01) と、西暦年の下 2 桁をこの順に並べたもの 与えられた数字列のフォーマットとして考えられるものが YYMM フォーマットのみである場合 YYMM を、 MMYY フォーマットのみである場合 MMYY を、 YYMM フォーマット と MMYY フォーマットのどちらの可能性もある場合 AMBIGUOUS を、 どちらの可能性もない場合 NA を出力してください。

私の回答はこちらです。

S = gets.to_s

int = S.scan(/.{1,#{2}}/)
 
if int[0].to_i.between?(1, 12) & int[1].to_i.between?(1, 12)
  puts 'AMBIGUOUS'
elsif !int[0].to_i.between?(1, 12) & !int[1].to_i.between?(1, 12)
  puts 'NA'
elsif !int[0].to_i.between?(1, 12) & int[1].to_i.between?(1, 12)
  puts 'YYMM'
else
  puts 'MMYY'
end

与えられるのは数字列という事でに関しては何も考慮せず、数字列の前方・後方の2桁がにあてはまるか?、つまり01〜12の間にあてはまるのか?だけ見れば良いと考えました。

まず、両方がMMか?否か?を判定した後に、片方づつMMか?判定する様にしています。

他の方の回答を見ると、case文で書かれていたり、正規表現を使ったり、三項演算子を用いてワンライナーで書いている人も居たりしました💦

中でも面白かったのは、下記の様にして、数字列の前方・後方の2桁を分割されているコードでした。

n = gets.to_i
a = n/100
b = n%100

例えば、入力値が「1990」の場合を想定します。

100(整数:integer型)で割ると、小数部分は切り捨てられるので、前方の「19」が得られます。

次に、100での剰余を求めると、後方の「90」が得られます。

これにより前方・後方の2桁を得る事が出来ます。

それを元に、初めのコードを組み替えると、下記の様になります。

S = gets.to_s

a = S/100
b = S%100
 
if a.between?(1, 12) & b.between?(1, 12)
  puts 'AMBIGUOUS'
elsif !a.between?(1, 12) & !b.between?(1, 12)
  puts 'NA'
elsif !a.between?(1, 12) & b.between?(1, 12)
  puts 'YYMM'
else
  puts 'MMYY'
end

先ほどよりスマートになりました。条件式部分もかなり改善の余地がありますが、長くなるので飛ばします。

C - Dice and Coin

問題文 すぬけ君は 1 〜 N の整数が等確率で出る N 面サイコロと表と裏が等確率で出るコインを持っています。すぬけ君は、このサイコロとコインを使って今から次のようなゲームをします。

まず、サイコロを 1 回振り、出た目を現在の得点とする。 得点が 1 以上 K − 1 以下である限り、すぬけ君はコインを振り続ける。表が出たら得点は 2 倍になり、裏が出たら得点は 0 になる。 得点が 0 になった、もしくは K 以上になった時点でゲームが終了する。このとき、得点が K 以上である場合すぬけ君の勝ち、 0 である場合すぬけ君の負けである。 N と K が与えられるので、このゲームですぬけ君が勝つ確率を求めてください。

私の回答がこちらです。

N,K = gets.chomp.split(" ").map(&:to_i);
 
win = []
 
N.times do |n|
  count = 0
  score = n + 1
  while score <= K - 1  do
    score *= 2
    count += 1
  end
  if count == 0
    win << N
  else
    win <<  2**count * N
  end
end
 
win_win = 0.to_f
 
win.each do |num|
  win_win += ( 1 / num.to_f )
end
 
puts win_win

変数名はテキトウです・・・

まず、サイコロの面の数だけ繰り返し処理をする様にtimesを記述します。

次に、勝利に必要なコイントスの回数を計算します。

最後は、コイントスの回数だけ2を累乗して、それにNを掛けます。

こうする事で、そのサイコロの目における勝利確率の分母が出せます(なお、コイントスする前に勝利が確定している場合は、サイコロの目だけを分母とします)

そして、それらの分母で1を割って、計算結果を全て合計すれば、確率が出せます(超ゴリ押し・・・)

他の方の回答で面白かったのは、reduce・Math・log2などを駆使して、解かれていた回答でした(どれも使った事が無い・・・)

reduce (Enumerable) - Rubyリファレンス

module Math (Ruby 2.6.0)

module function Math.#log2 (Ruby 2.6.0)

改善点

  • 標準入力を取得する部分など、テンプレート化できる箇所は、テンプレを利用する

  • コードテストしてから提出する(存在を知らなかった)

  • putsとかデバック用に書いた記述の消し忘れ注意

  • リファレンスを読もう!!(知らないメソッドが一杯!!)

《今日の学習進捗(3年以内に10000時間に向けて)》

javascript のライブラリやプラグインを用いる場面が最近増えているが、かなり苦戦している。公式のサンプルコードや別プロジェクトのコードを参考にして、動くところまでは実装出来るが、拡張性やパフォーマンスなどに対する考慮が十分であるか?確信が持てなかったり、かなり作業に時間を要してしまうという問題がある。土台となるjavascriptの知識を補う事が課題だと考えている。まずは、バックエンドの実装部分に関する知見を増やす事が優先だと考えているが、ゆくゆくはそちらの知見も増やしたい。

学習開始からの期間 :164日
今日までの合計時間:1588h
一日あたりの平均学習時間:9.7h
今日までに到達すべき目標時間:1498h
目標との解離:90h
「10,000時間」まで、

残り・・・「8412時間!」