CodeIQ:【誕生日問題10月】金山をたくさんプレゼント

私自身が表題の問題を解いた時のプログラムについて解説します。
問題の詳細は「【誕生日問題10月】金山をたくさんプレゼント」(CodeIQ)を参照してください。

問題の概要

問題を引用します。
問題
あなたは誕生日プレゼントとして、土地をもらえることになりました。
もらえる土地は、5×5マスの正方形の土地です。
世界は10×10のマスでできており、いくつかのマスには金山があります。金山の数が最大になる5×5マスは、1つしかありません。
最も多くの金山をゲットできるように、もらえる土地を探索するプログラムを書いてください。


以下、入力の例です。左上を {"x":0,"y":0} として、右下を {"x":9,"y":9} とします。「x」はx座標、「y」はy座標です。通常の土地は「w」の文字、金山のある土地は「G」の文字です。
これらは標準入力から、改行で区切られた文字として渡されます。

wwGwwwwwGG
Gwwwwwwwww
wwwwwwwwww
Gwwwwwwwww
wwwwGwwGww
wGwwwwwwww
wwwGGwwwww
wwwwwwGwww
wwwwGGwwww
GwwwGGwGwG

以下、出力の例です。最も多くの金山が得られる土地の左上の座標を、「{"x":3,"y":5,"g":8}」のように答えます。「x」はx座標、「y」はy座標、「g」は5×5マスの土地に含まれる金山の数です。
答えは、以下のように標準出力に出力してください。

{"x":3,"y":5,"g":8}

上記の答えを図解すると、下のような土地になります。

wwG wwwww GG
Gww wwwww ww
www wwwww ww
Gww wwwww ww
www wGwwG ww
   +-----+
wGw|wwwww|ww
www|GGwww|ww
www|wwwGw|ww
www|wGGww|ww
Gww|wGGwG|wG
   +-----+

私のプログラム

Rubyで解答しています。

#!/usr/bin/ruby

def getCount(map, r, c, w)
	area = []
	for i in r...(r+w)
		area += map[i][c...(c+w)]
	end
	return area.count("G")
end

def solve(map)
	cols = map[0].size
	rows = map.size
	w = 5

	mx = 0
	pos = [-1,-1]

	for i in 0..(rows-w)
		for j in 0..(cols-w)
			c = getCount(map, i, j, w)
			if c > mx then
				mx = c
				pos = [i,j]
			end
		end
	end

	return '{"x":' + pos[1].to_s + ',"y":' + pos[0].to_s + ',"g":' + mx.to_s + '}'
end

# main
map = []
while line = gets
	line.strip!
	if line.empty? then next end

	map << line.split("")
end

ret = solve(map)
puts ret

解説

簡単な問題です。特に悩むようなところはありません。

考え方

マップのサイズが10×10しかなく、調べる範囲が5×5固定なので総当たりで調べても時間が問題になることはありません。なので端から端まで調べれば良いだけです。

main

入力値を文字の配列の配列(map)にし、solve()に渡します。
solve()が結果文字列を返すのでそれを印字して終わります。

solve(map)

引数mapは入力値を文字の配列の配列にしたものです。
colsは入力値の列数、rowsは行数で別に10固定にしても良かったのですが入力値から取得するようにしました。
wは探索する領域の行列のサイズ5です。
mxはGが最大数を保持する変数で、posはその座標です。

行列を1マスずつずらしならがループし(19〜27行目)、getCount()で現在の領域のGを数えます。その数がmxより大きければmxとposを更新します。
ループを抜けた時点のmxとposを出力形式にフォーマットして返します。

getCount(map, r, c, w)

引数mapは入力値を文字の配列の配列にしたものです。
rは現在の座標の行、cは列です。
wは探索する領域の行列のサイズです。5固定でも構いませんが呼び出し元から渡すようにしました。

5〜7行目は渡された座標からw行目までの各行(i)の、座標の列(c)からw文字までを1つの配列にまとめています。問題の図解の枠で囲った部分を1次元の配列にしているわけです。
Rubyの配列にはcount()という便利なメソッドがあるので前記の配列からGを数えて返せば当該領域のGの数になります。

雑感

もう少し頭の良い(総当たりではない)方法があるのかと思って投稿しましたが、評価結果にあったコメントをみたら総当たりが出題者の予想した解答だったみたいです。
問題とは関係ありませんが、時々RubyとかPythonでもC言語風のforループが使いたくなります。
19行目や20行目のループですが、
for(i=0; i+w<10; i++)
みたいに書きたいなと、もう少し言うとループカウンタとブレーク条件を別にしたいと思うことが時々あります。まぁ、バグの元になりやすいからそういう書き方は許可しないというのもわかりますが。