summer_tree_home

Check iOでPython3をマスターするぜっ

Golden Pyramid (Home) - ピラミッドの探索

Home島に新しい問題が追加されていた。

どんな問題?

Golden Pyramid
http://www.checkio.org/mission/golden-pyramid/

ピラミッドの各部屋に数字が書かれている。ピラミッドの上から下まで、数字の合計値が最も大きくなるようなルートを通ったとき、数字の合計値はいくつになるかを求めよ。
ピラミッドでは、現在の右下の部屋か左下の部屋にしか進めない。

引数は、ピラミッドのデータが tuple of tuple で与えられる。
ピラミッドは20段以下、各部屋の数字は0<x<10の整数となる。
戻り値は、数字の合計値の最大値を返す。

例題:

count_gold((
    (1,),
    (2, 3),
    (3, 3, 1),
    (3, 1, 5, 4),
    (3, 1, 3, 1, 3),
    (2, 2, 2, 2, 2, 2),
    (5, 6, 4, 5, 6, 4, 3)
)) == 23
count_gold((
    (1,),
    (2, 1),
    (1, 2, 1),
    (1, 2, 1, 1),
    (1, 2, 1, 1, 1),
    (1, 2, 1, 1, 1, 1),
    (1, 2, 1, 1, 1, 1, 9)
)) == 15
count_gold((
    (9,),
    (2, 2),
    (3, 3, 3),
    (4, 4, 4, 4)
)) == 18

どうやって解く?

ヒントに動的計画法(Dynamic Programming、DP)の問題だと書かれている。先日のStair stepsでも出てきた用語だ。確かに問題の内容も似ている。今度こそDPとやらを使って解いてみよう。

動的計画法

上の部屋から順に、頂上から現在地までの合計値の最大値を代入していく。
自分の右上の部屋と、左上の部屋の数字を比べ、大きい方を自分の部屋の数字と足していく。
最下層に着いたら、最下層の部屋の最大値が答えとなる。

def count_gold(pyramid):
    pyramid = [list(t) for t in pyramid]  # to list
    for y in range(1, len(pyramid)):
        for x in range(len(pyramid[y])):
            up_right_room = 0 if x >= len(pyramid[y - 1]) else pyramid[y - 1][x]
            up_left_room = 0 if x == 0 else pyramid[y - 1][x - 1]
            pyramid[y][x] += max(up_right_room, up_left_room)
    return max(pyramid[-1])

http://www.checkio.org/mission/golden-pyramid/publications/natsuki/python-3/first/

pyramid変数そのものを書き換えるため、tupleからlistに変換している。


他のやり方も考えてみた。

全ルートを試す

すべてのルートの合計値を計算して、最大値を求める方法を考えてみた。
一階層降りる毎に、左に行くなら0、右に行くなら1の選択を、ピラミッドの高さ-1回繰り返す。これは、itertools.productを使えばいい。生成された左(0)か右(1)かの並びを、accumulateで加算すればindex値に変換できる。頂上は一部屋しかないので、[0]を追加している。

# 例えば、左→左→右→右→左 に進む場合
d = (0,0,1,1,0)  
# accumulate(d) = (0,0,1,2,2)
route = [0] + list(accumulate(d))  # [0,0,0,1,2,2]

全ルートで合計値を計算して、最大値を求める。

from itertools import product, accumulate
 
def count_gold(pyramid):
    def get_sum(route):
        # ルートから合計値を取得
        return sum(pyramid[i][route[i]] for i in range(len(pyramid)))
 
    all_routes = ([0] + list(accumulate(d)) for d in product([0, 1], repeat=len(pyramid) - 1))
    return max(get_sum(route) for route in all_routes)

http://www.checkio.org/mission/golden-pyramid/publications/natsuki/python-3/second/

少し時間はかかったが無事にクリア。