도둑질 문서의 문제 1 풀이 코드다. if __name__ == "__main__": 아래가 자체 테스트다.

"""도둑질 (프로그래머스 42897) 풀이와 검증."""
import random
import time


def best_line(a):
    """한 줄로 놓인 집에서 이웃한 두 집을 털지 않는 최대 금액."""
    prev2, prev1 = 0, 0                       # dp[i-2], dp[i-1]
    for x in a:
        prev2, prev1 = prev1, max(prev1, prev2 + x)
    return prev1


def solution(money):
    # 첫 집과 마지막 집은 이웃이라 둘 중 하나는 안 턴다
    return max(best_line(money[:-1]), best_line(money[1:]))


def by_subsets(money):
    n = len(money)
    best = 0
    for mask in range(1 << n):
        ok = all(not ((mask >> i) & 1 and (mask >> ((i + 1) % n)) & 1) for i in range(n))
        if ok:
            best = max(best, sum(money[i] for i in range(n) if (mask >> i) & 1))
    return best


if __name__ == "__main__":
    assert solution([1, 2, 3, 1]) == 4 == by_subsets([1, 2, 3, 1])
    # 6단계 추적: [2, 7, 9, 3, 1] 한 줄의 dp
    dp, p2, p1 = [], 0, 0
    for x in [2, 7, 9, 3, 1]:
        p2, p1 = p1, max(p1, p2 + x)
        dp.append(p1)
    assert dp == [2, 7, 11, 11, 12]
    assert solution([2, 7, 9, 3, 1]) == 11 == by_subsets([2, 7, 9, 3, 1])
    assert (best_line([2, 7, 9, 3]), best_line([7, 9, 3, 1])) == (11, 10)
    alt = [5, 1, 1, 5, 1, 1]                                  # 짝수·홀수 번째 합은 둘 다 7
    assert sum(alt[0::2]) == sum(alt[1::2]) == 7 and solution(alt) == 10 == by_subsets(alt)
    rng = random.Random(42897)
    for _ in range(3000):
        m = [rng.randint(0, 20) for _ in range(rng.randint(3, 12))]
        assert solution(m) == by_subsets(m), m
    big = [rng.randint(0, 1000) for _ in range(1_000_000)]
    t0 = time.perf_counter()
    solution(big)
    t = time.perf_counter() - t0
    assert t < 10, t
    print(f"1M houses: {t:.2f}s")
    print("ALL CHECKS PASSED")