紙一重の積み重ね

35歳のエンジニアがなれる最高の自分を目指して、学んだことをこつこつ情報発信するブログです。

【まずはBeginners Selectionから】35歳だけどAtCoderはじめました #AtCoder

f:id:yokoyantech:20180417130743p:plain

はじめに

育児の合間にAtCoderのBeginners Selectionの問題を少しづつ解いています。

qiita.com

やってみた感想

自分のプログラムの書けなさっぷりに衝撃を受けました。

SIerでプロジェクトリーダーやPJ管理の仕事ばかりしていたため、プログラムを書いてきた時間が圧倒的に足りていません。今の自分のレベルは、A問題やB問題がかろうじて解けるレベルだと思います。

今の自分の立ち位置を真摯に受け止め、少しずつレベルアップしていきたいと思います。

第 1 問: ABC 086 A - Product (100 点)

問題

A - Product

書いたプログラム(Ruby)

# 入力:整数a b
# 出力:積が奇数なら Odd と、 偶数なら Even と出力せよ。

a,b = gets.chomp.split(" ").map(&:to_i)
c = a * b
if c % 2 == 0
    puts("Even")
else 
    puts("Odd")
end

デバッグ

$ ruby ABC086A/answer.rb
3 4
Even
$ ruby ABC086A/answer.rb
1 21
Odd

結果

無事にACを取れました。

類題:ABC 088 A - Infinite Coins (倍数判定の発展として、余りを計算します)

問題

A - Infinite Coins

書いたプログラム(Ruby)

# 出力:E869120 の持っている 1 円硬貨と 500 円硬貨だけで, ちょうど N 円を支払うことができるならば Yes, そうでないならば No を出力しなさい.

# 支払金額
n = gets.to_i
# 500円は無限
# 持っている1円の枚数
a = gets.to_i

if n % 500 <= a
    puts("Yes")
else
    puts("No")
end

デバッグ

$ ruby abc088_a/answer.rb
2018
218
Yes
$ ruby abc088_a/answer.rb
2763
0
No

結果

無事にACを取れました。

第 2 問: ABC 081 A - Placing Marbles (100 点)

問題

A - Placing Marbles

書いたプログラム(Ruby)

forで書く。1行で書くこともできる。

# 1の場合はビー玉を置く
# 文字列の取得
str = gets.chomp
num = 0

# for文を使って書く
for i in 0..str.size do
    num += 1 if str[i] == '1'
end

# 出力
p num

# 1行で書く
# p gets.chomp.count("1")

デバッグ

$ ruby abc081_a/answer.rb 
101
2
$ ruby abc081_a/answer.rb 
000
0

第 3 問: ABC 081 B - Shift Only (200 点)

問題

B - Shift only

書いたプログラム(Ruby)

# 与えられた整数がすべて偶数なら、すべての整数を2で割ったものに置き換える

# 入力
n = gets.chomp.to_i
nums = gets.chomp.split(" ").map(&:to_i)
count = 0

# 整数かどうか判断
# 偶数ならevenメソッド
# 奇数ならoddメソッド
# すべて2で割れるなら2で割ったものに置換する
# a,b,cいずれかが2で割り切れなくなるまで繰り返す

# selectメソッド
# 条件に合致する要素を取り出す

# collectメソッド
# 要素の数だけ繰り返しブロックを実行し、ブロックの戻り値を集めた配列を作成する
# collect!でレシーバ自身を変更する
while nums.select(&:odd?).empty? do
    nums.collect! {|num| num /= 2}
    count += 1
end

# 出力
p count

第 4 問: ABC 087 B - Coins (200 点)

問題

B - Coins

書いたプログラム(Ruby)

# 500円の枚数
A = gets.chomp.to_i
# 100円の枚数
B = gets.chomp.to_i
# 50円の枚数
C = gets.chomp.to_i
# xは50の倍数
X = gets.chomp.to_i

# a,b,cの組合せの中に、xがあるか?

count = 0

for a in 0..A do
    for b in 0..B do
        for c in 0..C do
            # 金額計算
            count += 1 if (500 * a) + (100 * b) + (50 * c) == X
        end
    end
end

# 出力
p count

第 5 問: ABC 083 B - Some Sums (200 点)

問題

B - Some Sums

中間成果物のデバッグ

2.4.0 :003 > def digit_sum n
2.4.0 :004?>       sum = 0
2.4.0 :005?>       while n > 0 do
2.4.0 :006 >               sum += n % 10
2.4.0 :007?>             n /= 10
2.4.0 :008?>         end
2.4.0 :009?>       sum
2.4.0 :010?>   end
 => :digit_sum 
2.4.0 :011 > (1..20).map{ |i| [i, digit_sum(i)]}
 => [[1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6, 6], [7, 7], [8, 8], [9, 9], [10, 1], [11, 2], [12, 3], [13, 4], [14, 5], [15, 6], [16, 7], [17, 8], [18, 9], [19, 10], [20, 2]] 
2.4.0 :013 > (1..20).map{ |i| [i, digit_sum(i)]}.select { |d| 2 <= d[1] && d[1] <= 5 }                                                                                                                                                
 => [[2, 2], [3, 3], [4, 4], [5, 5], [11, 2], [12, 3], [13, 4], [14, 5], [20, 2]] 
2.4.0 :014 > (1..20).map{ |i| [i, digit_sum(i)]}.select { |d| 2 <= d[1] && d[1] <= 5 }.inject(0) { |sum, d| sum + d[0] }
 => 84 

書いたプログラム(Ruby)

N,A,B = gets.chomp.split(' ').map(&:to_i)
# N以下の整数
# 各桁の和が、A以上B以下の合計を出力する

def find_sum_of_digits n
    sum = 0
    while (n > 0)
        # 10で割った余りを足すのが定石
        sum += n % 10
        n /= 10
    end
    return sum
end

sum = 0
total = 0

for n in 1..N do
    # 各桁の和を求める
    sum = find_sum_of_digits n
    if sum >= A && sum <= B
        total += n
    end
    n += 1
end

p total

別の解き方

injectメソッドを使って解くともっと楽。

qiita.com

injectメソッドとは

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

injectメソッドは、ブロックを使って繰り返し計算を行うのに使います。ブロックに順に「要素1、要素2」、「ブロックの前回の戻り値、要素3」、「ブロックの前回の戻り値、要素4」、...を渡します。メソッドの戻り値はブロックが最後に返した値になります。

引数initで初期値を指定すると、ブロックに「初期値、要素1」、「ブロックの前回の戻り値、要素2」、「ブロックの前回の戻り値、要素3」、...を渡します。

2.4.0 :015 > numbers = [4, 3, 9, 8, 5, 6, 1, 7, 2]
 => [4, 3, 9, 8, 5, 6, 1, 7, 2] 
2.4.0 :016 > puts numbers.inject {|sum, n| sum + n }
45
 => nil 
2.4.0 :018 > puts numbers.inject(100) {|diff, n| diff - n}
55
 => nil 

参考サイト

blog.toshimaru.net

2.4.0 :019 > sum = 0
 => 0 
2.4.0 :020 > (1..10).each {|i| sum += i}
 => 1..10 
2.4.0 :021 > puts sum
55
 => nil 

# こう書いても同じ
2.4.0 :024 > (1..10).inject(0){|sum, i| sum += i}
 => 55 
 
# 初期値は省略可能
2.4.0 :025 > (1..10).inject {|sum,i| sum += i}
 => 55 

# シンボルを渡すこともできる。すごい!
2.4.0 :026 > (1..10).inject(:+)
 => 55 

おわりに

本エントリでは、AtCoder Beginners Selectionの5問目までを解きました。プログラミングも急に書けるようになるわけではなく、毎日地道にこつこつ書いてこそだと思います。プログラミングも紙一重の努力を積み重ねます!