【AtCoder:1回目】AtCoder Beginner Contest 126の振り返り(Ruby)
【結論】
・Rubyで回答しています。
・ABCしか解けなかったので、振り返りはその3問のみです。
・解説ではなく、自身の振り返りです(クソコードしか書いてない・・・)
【目次】
- AtCoder Beginner Contest 126の振り返り
- 結果
- A - Changing a Character
- B - YYMM or MMYY
- C - Dice and Coin
- 改善点
【本題】
AtCoder Beginner Contest 126の振り返り
初めてAtCoderのコンテストに挑戦したので、その振り返しをしたいと思います。
今回挑戦したのは、5/19(日)に開催された「AtCoder Beginner Contest 126」です。
AtCoder Beginner Contest 126 - AtCoder
結果
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などを駆使して、解かれていた回答でした(どれも使った事が無い・・・)
https://ref.xaio.jp/ruby/classes/enumerable/reduce
module Math (Ruby 3.2 リファレンスマニュアル)
Math.#log2 (Ruby 3.2 リファレンスマニュアル)
改善点
標準入力を取得する部分など、テンプレート化できる箇所は、テンプレを利用する
コードテストしてから提出する(存在を知らなかった)
putsとかデバック用に書いた記述の消し忘れ注意
リファレンスを読もう!!(知らないメソッドが一杯!!)