Ruby
競技プログラミング

Ruby で競技プログラミングをするときのまとめ(多次元配列編)

  • リクエストに応じて追記していきます。
  • 環境は AtCoder を想定しています。なお AtCoder の Ruby のバージョンは 2018年10月現在 2.3.3 です。

入力について

入力が地図のパターン

$H$, $W$ は整数
$c_{i,j}$ はなんらかの文字

$H \quad W$
$c_{1,1}c_{1,2}...c_{1,W}$
$c_{2,1}c_{2,2}...c_{2,W}$
$:$
$c_{H,1}c_{H,2}...c_{H,W}$

文字列の配列にしたいとき

H, W = gets.split.map(&:to_i)
c = H.times.map { gets.chomp }

1文字ずつの二次元配列にしたいとき

H, W = gets.split.map(&:to_i)
c = H.times.map { gets.chomp.chars }

入力が数の二次元配列のパターン

$H$, $W$ は整数
$a_{i,j}$ は整数

$H \quad W$
$a_{1,1} \quad a_{1,2} \quad ... \quad a_{1,W}$
$a_{2,1} \quad a_{2,2} \quad ... \quad a_{2,W}$
$...$
$a_{H,1} \quad a_{H,2} \quad ... \quad a_{H,W}$

H, W = gets.split.map(&:to_i)
a = H.times.map { gets.split.map(&:to_i) }

入力が幅と高さのみで、二次元配列を自分で作るパターン

$H$, $W$ は整数

$H \quad W$

H, W = gets.split.map(&:to_i)
a = H.times.map {|i|
  W.times.map {|j|
    # i, j による初期値
  }
}

処理について

文字列の配列の各文字ごとに何かしたいとき

文字だけでいいとき

c.each {|line|
  line.chars {|ch|
    # ch の処理
  }
}

インデックスが必要なとき

H.times {|i|
  W.times {|j|
    # c[i][j] の処理
  }
}

二次元配列の各要素ごとに何かしたいとき

要素だけでいいとき

a.each {|row|
  row.each {|elem|
    # elem の処理
  }
}

インデックスが必要なとき

H.times {|i|
  W.times {|j|
    # a[i][j] の処理
  }
}

両方(HW がわかっているなら上で十分)

a.each_with_index {|row, i|
  row.each_with_index {|elem, j|
    # i, j, elem の処理
  }
}

地図とかで上下左右を参照するとき

H.times {|i|
  W.times {|j|
    [
      [-1,  0], # 上
      [ 1,  0], # 下
      [ 0, -1], # 左
      [ 0,  1], # 右
    ].each {|di, dj|
      if 0<=i+di && i+di<H && 0<=j+dj && j+dj<W
        # c[i + di][j + dj] の処理
      else
        # 範囲外のときの処理
      end
    }
    # c[i][j] の処理
  }
}

地図とかで斜めも含め八方を参照するとき

H.times {|i|
  W.times {|j|
    (-1..1).each {|di|
      (-1..1).each {|dj|
        if di==0 && dj==0
          # c[i][j] の処理
        elsif 0<=i+di && i+di<H && 0<=j+dj && j+dj<W
          # c[i + di][j + dj] の処理
        else
          # 範囲外のときの処理
        end
      }
    }
    # c[i][j] の処理
  }
}

インデックスについて

注意1:範囲外のアクセス(size 以上のとき)

a[i][j] のようにアクセスしたとき、ia.size 以上であると a[i]nil を返し、そのまま [j] でアクセスしてエラーを起こします。

undefined method `[]' for nil:NilClass (NoMethodError)

対処法として、i の大きさを確認するか、a[i]nil でないことを確認しましょう。

注意2:範囲外のアクセス(0 未満のとき)

注意1では触れませんでしたが、Ruby では 配列のインデックスに負数を指定することができます。

-1 が一番最後で、後ろから -1, -2, ... という具合で指定できます。

これは便利なときと不便なときがあって、地図が上下左右でつながっている(ドラクエみたいな)問題では便利ですが、そうでないときは不便です。

不要なときは i の大きさを確認しましょう。a[i]nil であるとは限らなくなるので、 nil チェックは使えません。

出力について

c が文字列の配列のとき

puts c

c が文字の二次元配列のとき

puts c.map(&:join)

a が数値の二次元配列で、スペース区切りで出力するとき

puts a.map {|line| line.join(" ") }

または

a.each {|line| puts line.join(" ") }