지형 편집 문서의 문제 1 풀이 코드다. if __name__ == "__main__": 아래가 자체 테스트다.
"""지형 편집 (프로그래머스 12984) 풀이와 검증."""
import random
import time
from bisect import bisect_left
def solution(land, P, Q):
h = sorted(x for row in land for x in row)
n = len(h)
S = [0]
for x in h:
S.append(S[-1] + x) # S[i]: 낮은 쪽 i칸의 높이 합
best = None
for i, t in enumerate(h): # 목표 높이 후보: 지금 있는 높이들
if i > 0 and h[i - 1] == t:
continue
k = bisect_left(h, t) # t보다 낮은 칸 수
add = t * k - S[k] # 낮은 칸을 t까지 채우는 블록 수
remove = (S[n] - S[k]) - t * (n - k) # 높은 칸을 t까지 깎는 블록 수
cost = add * P + remove * Q
if best is None or cost < best:
best = cost
return best
def by_every_height(land, P, Q):
cells = [x for row in land for x in row]
best = None
for t in range(min(cells), max(cells) + 1):
cost = sum((t - x) * P if x < t else (x - t) * Q for x in cells)
best = cost if best is None else min(best, cost)
return best
if __name__ == "__main__":
assert solution([[1, 2], [2, 3]], 3, 2) == 5 == by_every_height([[1, 2], [2, 3]], 3, 2)
assert solution([[4, 4, 3], [3, 2, 2], [2, 1, 0]], 5, 3) == 33
# 6단계 추적: 예시 2의 높이 후보별 비용
land = [[4, 4, 3], [3, 2, 2], [2, 1, 0]]
cells = sorted(x for r in land for x in r)
assert cells == [0, 1, 2, 2, 2, 3, 3, 4, 4]
costs = {t: sum((t - x) * 5 if x < t else (x - t) * 3 for x in cells) for t in range(0, 5)}
assert costs == {0: 63, 1: 44, 2: 33, 3: 46, 4: 75}, costs
S = [0]
for x in cells:
S.append(S[-1] + x)
rows = []
for t in range(0, 5):
k = bisect_left(cells, t)
rows.append((t, k, t * k - S[k], (S[9] - S[k]) - t * (9 - k)))
assert rows == [(0, 0, 0, 21), (1, 1, 1, 13), (2, 2, 3, 6), (3, 5, 8, 2), (4, 7, 15, 0)], rows
rng = random.Random(12984)
for _ in range(2000):
N = rng.randint(1, 5)
land = [[rng.randint(0, 12) for _ in range(N)] for _ in range(N)]
P, Q = rng.randint(1, 10), rng.randint(1, 10)
assert solution(land, P, Q) == by_every_height(land, P, Q), (land, P, Q)
# 300×300, 높이 10억까지
land = [[rng.randint(0, 10 ** 9) for _ in range(300)] for _ in range(300)]
t0 = time.perf_counter()
solution(land, 37, 81)
t = time.perf_counter() - t0
assert t < 10, t
print(f"300x300: {t:.2f}s")
print("ALL CHECKS PASSED")