Google DevQuiz 2011 解答例「一人ゲーム」編

IT業界人、特にプログラマーにとっては恒例のイベントとなりつつある、GoogleのDevQuiz。9月12日の午前10時で解答の送信が締め切られ、有志による解答例の公開(いわゆる”晒し”)が始まっています。

ということで、私も自分の解答を”晒し”てみることにします。まずは、分野別クイズの「一人ゲーム」から。

この「一人ゲーム」のルールは、

  • 問題として10個以下の整数が渡される
  • プレイヤーは
    • 全ての5の倍数(0を含む)を取り除く
    • 全ての数字を半分にする(端数切り捨て)

    のどちらかの操作ができる。

  • 問題の整数が全て取り除かれれば終了
  • 終了までの最短の操作回数を答える

というもの。

例えば問題として、

10 11

の2つの整数が渡された場合、

10 11 ―(取る)→ 11 ―(半分)→ 5 ―(取る)→ 終了 なら3手
10 11 ―(半分)→ 5 5 ―(取る)→ 終了 なら2手

なので、答えは「2」となります。

問題のファイルは、先頭行に問題数、次の行から問題の整数の数、問題の整数のリストの繰り返しとなり、問題数は全部で100問。解答は、各行に各問題の解答の手数を書いた100行のファイルで送信します。

以下、私の解答コード。言語はPythonです。

def solve(nums, step):
	step += 1
	c = 0
	for num in nums:
		if num % 5 == 0:
			c += 1
	if c == len(nums):
		return step
	hnums = []
	for n in range(len(nums)):
		hnums.append(nums[n] / 2)
	hsteps = solve(hnums, step)
	if c > 0:
		rnums = []
		for n in range(len(nums)):
			if nums[n] % 5 != 0:
				rnums.append(nums[n])
		rsteps = solve(rnums, step)
		return hsteps if rsteps > hsteps else rsteps
	return hsteps

f = open('numbers', 'r')
r = open('result', 'w')
for q in range(int(f.readline())):
	f.readline()
	nums = []
	for num in f.readline().split():
		nums.append(int(num))
	r.write(str(solve(nums, 0)))
	r.write('\n')

コードの実行は22行目からスタートし、問題のファイルを開いて問題の整数を読み込み、solve関数を呼びます。
solve関数では、整数リストに取り除ける5の倍数があるかどうかをチェックし、全て取り除けるなら終了して手数を返します。もし数字が残るなら、全ての数字を半分にした整数リストをsolve関数に渡して再帰呼び出しし、手数を得ます(手数1)。さらに、もし取り除ける数字があった場合には5の倍数を取り除いた整数リストをsolve関数に渡して再帰呼び出しし、手数を得ます(手数2)。そして、手数1と手数2のうち、少ない手数を返します。

つまり、「取り除く」or「半分にする」の2分岐の全パターンを再帰で実行し、最も少ない手数を返すというプログラムです。最大に分岐しても2の21乗(=2097152)通り程度なので、普通のPCなら一瞬で答えが出ます。

Pythonはあまり慣れていないので、Pythonらしくないコードかもしれませんが、参考まで。

※追記
Pythonは
[n for n in nums if n % 5 != 0]
って書き方ができるんですね。

多少Pythonらしくなるように書き直してみました。

def solve(nums, step):
	step += 1
	rnums = [n for n in nums if n % 5 != 0]
	if len(rnums) == 0:
		return step
	hsteps = solve([n / 2 for n in nums], step)
	if len(rnums) != len(nums):
		rsteps = solve(rnums, step)
		return hsteps if rsteps > hsteps else rsteps
	return hsteps

if __name__ == "__main__":
	f = open('numbers', 'r')
	r = open('result', 'w')
	for q in range(int(f.readline())):
		f.readline()
		r.write(str(solve([int(num) for num in f.readline().split()], 0)))
		r.write('\n')

※追記2
最短の解を見つけるなら、幅優先探索の方が有利なんですね。
後出しになりますが、幅優先に書き直し。

def solve(anums):
	current = [anums]
	step = 0
	while 1:
		step += 1
		next = []
		for nums in current:
			rnums = [n for n in nums if n % 5 != 0]
			if len(rnums) == 0:
				return step
			if len(rnums) < len(nums):
				next.append(rnums)
			next.append([n / 2 for n in nums])
		current = next

if __name__ == "__main__":
	f = open('numbers', 'r')
	r = open('result', 'w')
	for q in range(int(f.readline())):
		f.readline()
		r.write(str(solve([int(num) for num in f.readline().split()])))
		r.write('\n')

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*