晴れときどき晴れ

プログラミングと日常についてマイペースにあげます

競技プログラミング(rubyの多次元配列で困った)

競技プログラミングに最近ハマっている。

AOJAtCoderを中心に問題を解いている
特にAOJのIntroductionは新しい言語を学ぶ時に試しにやってみると言語の曖昧な理解が少しは解消されるんじゃないかなと思った。
というのも、この問題

A大学は1フロア10部屋、3階建ての公舎4棟を管理しています。公舎の入居・退去の情報を読み込み、各部屋の入居者数を出力するプログラムを作成して下さい。

n件の情報が与えられます。各情報では、4つの整数b, f, r, vが与えられます。これは、b棟f階のr番目の部屋にv人が追加で入居したことを示します。vが負の値の場合、v人退去したことを示します。

最初、全ての部屋には誰も入居していないものとします。

Input
最初の行に情報の数 n が与えられます。

続いて n 件の情報が与えられます。各情報には4つの整数 b, f, r, v が空白区切りで1行に与えられます。

Output
4棟について入居者数を出力して下さい。各棟について、1階、2階、3階の順に入居者数を出力します。各階については、1番目、2番目、・・・、10番目の部屋の入居者数を順番に出力します。入居者数の前には1つの空白を出力して下さい。また、各棟の間には####################(20個の#)で区切って下さい。

最初に僕が書いたコードはこんな感じ

def zeros(m, n)
  a = Array.new(m, Array.new(n, 0))
  a
end

a = [zeros(3, 10), zeros(3, 10), zeros(3, 10), zeros(3, 10)]

n = gets.chomp.to_i
n.times do
  input = gets.chomp.split(' ').map { |e| e.to_i - 1 }
  a[input[0]][input[1]][input[2]] += input[3] + 1
end

count = 0
a.each do |i|
  i.each do |j|
    puts j.join(' ')
  end
  count += 1
  puts '#' * 20 if count < a.size
end

しかし、これだと変なことになって
例えば入力が
1
1 1 3 8
だった時、正解の出力は

 0 0 8 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0
####################
 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0
####################
 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0
####################
 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0

だが、僕のコードでは

 0 0 8 0 0 0 0 0 0 0
 0 0 8 0 0 0 0 0 0 0
 0 0 8 0 0 0 0 0 0 0
####################
 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0
####################
 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0
####################
 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0

となってしまう
これは多次元配列を作成する部分に問題があった
参照はこちら
簡単に言うと
Array.new(m, Array.new(n, 0))
では配列のリファレンスが同じ部分を指してしまうため、ひとつの変更が他の部分にも現れてしまう
Array.new(m).map { Array.new(n, 0) } とすれば大丈夫でした

文法書を眺めるだけだとこういう所まで気がまわらないので、やはり競技プログラミングで試しに使ってみることは大切なんだなという話でした