Ruby

paizaで女の子(安藤杏たん)にRubyを使って水着をプレゼントしよっ♡

More than 1 year has passed since last update.

:warning: この記事はネタバレ要素を含んでおります。 :warning:

突然ですが、皆様、彼女いますか?

え?僕ですか?はい!います! 証拠はこちら。

というわけで、彼女が居ない皆様、持ち前のプログラミング技術で彼女作りましょう!

https://paiza.jp/poh/ando

今回は、全ての問題をRubyで攻略していきます。

中級 paizaランクB相当問題

メガネ

入力
4
0 0 1 0
0 1 1 0
0 1 0 1
1 1 1 0
3
0 1 1
0 1 0
1 1 1

出力
1 0

このように入力された2つの2次元配列のデータから、1つ目と2つ目を比較した時、1つ目の2次元配列の中から2つ目に入力された2次元配列が完全一致する箇所を探し出し、その箇所の左上の座標を取り出すという問題でした。

以下、僕が考えた解法です。

  1. 入力されたデータから2次元配列を作る
  2. 一回目の入力と二回目の入力のマスの数の差をとる
  3. マスの数の差**2回ループを回しそれぞれの行の配列を比較
  4. 縦にm回連続で一致して居た時の1回目の配列のyxを出力する

これをそのままコードに落とす。

glasses.rb
n = gets.to_i
arr1 = []
n.times { arr1 << gets.to_s.chomp }
arr1.map! { |a| a.split(' ') }

m = gets.to_i
arr2 = []
m.times { arr2 << gets.to_s.chomp }
arr2.map! { |a| a.split(' ') }

i = n - m + 1
yx = []
flag = true
i.times do |j|
  i.times do |k|
    flag1 = true
    flag2 = true
    m.times do |l|
      flag1 &= arr1[j + l][k..k + m - 1] == arr2[l]
      if flag1 && flag2
        yx = [j, k]
        flag2 = false
      end
    end
    if flag && flag1 && !flag2
      flag = false
      print yx.join(' ')
    end
  end
end

はい、どんどん行きましょう。

サンタ服

幅 X cm、奥行き Y cm、高さ Z cm の直方体の形をしたケーキがあります。 このケーキに「側面と平行な方向」および「前面と平行な方向」に何回か包丁を入れて、小さなケーキに切り分けることを考えます。 上面と平行な方向(水平方向)には包丁を入れません。

包丁を入れる場所が与えられたとき、切り分けられたケーキの中で最も体積が小さいものを求めてください。

という問題でした。

入力
10 10 10 2
0 3
0 8

出力
200

僕が考えた解法

  1. x軸と平行に切る時のデータとy軸と平行に切る時のデータをそれぞれ別の配列に格納する
  2. 1で作成したそれぞれの配列をソートする
  3. 2で作成したソートされたデータを用いて、x軸とy軸それぞれにおいて、包丁で切った時の間隔が一番狭い時の間隔をとりだす
  4. 3で取り出したデータとzを掛けあわせる

そしてまたそのままコードに落とします。

santa.rb
x, y, z, n = gets.to_s.split(' ').map(&:to_i)

arr = []
n.times { arr << gets.to_s.split(' ') }

xs = [0]
ys = [0]

arr.length.times do |i|
  if arr[i][0] == '0'
    xs << arr[i][1].to_i
  else
    ys << arr[i][1].to_i
  end
end

xs << x
ys << y

xs = xs.sort
ys = ys.sort

dx = xs.last
xs.length.times do |i|
  break if i == xs.length - 1
  dx = xs[i + 1] - xs[i] if i == 0 || dx > xs[i + 1] - xs[i]
end

dy = ys.last
ys.length.times do |i|
  break if i == ys.length - 1
  dy = ys[i + 1] - ys[i] if i == 0 || dy > ys[i + 1] - ys[i]
end

print dx * dy * z

はい。

上級 paizaランクA相当問題(水着の問題)

水着の問題です。

N が与えられたとき、N! のすべての桁の代わりに、N! の最下位桁から続く0 をすべて除いた値の下位9桁を求めるプログラムを作成してください。
9桁ではあるが先頭が0であるような場合は先頭の0を取り除いた値を出力してください。先頭に0のついた値を出力すると誤答となります。

僕はこの問題で詰まりました。

詰まっていた原因

まともに階乗を計算しようとしていたことです。

例えば100万近い値を入力した時、最終的にこの階乗計算は膨大な値となり、計算に異常な時間が掛かるようになります。

その結果、Test Case3迄はなんとか通す事ができましたが、Test Case4以降がタイムアウトとなり、攻略出来ませんでした。

この問題ではまともに階乗の計算をする必要が無いのです。

要は、9桁以内で先頭と末尾に0がついていなければ良いのです。

僕の考えた解法

  1. 通常の階乗計算の中で一回ごとに末尾のゼロと先頭のゼロを取り除いた15桁程の数値を返すようにする
  2. 最後に9桁取り出して先頭のゼロを取り除き出力する
mizugi.rb
print (1..gets.to_i).inject { |s, i| 
  (s * i).to_s.reverse.to_i.to_s[0, 11].reverse.to_i
}.to_s.reverse.to_i.to_s[0, 9].reverse.to_i

スクリーンショット 2015-12-10 17.51.35.png

散々悩んでいたのに一行で書けてしまった。

感想

考える過程で、どの処理に時間がかかっているかをそれぞれ明確にしていくべきだった。

ベンチマークをとって数値化して明らかにしていく作業をするのが遅かったので、早めにやっておけばもっとスムーズに解法にたどり着いていたかも。