1
# Runtime: 514 ms (Top 80.64%) | Memory: 14.5 MB (Top 44.35%)2
from graphlib import TopologicalSorter, CycleError5
Corner = Tuple[int, int]6
Rect = Tuple[Corner, Corner] # [upper-left, lower-right (non-inclusive)]7
Layer = Tuple[Color, Rect]11
def isPrintable(self, targetGrid: List[List[int]]) -> bool:12
# Runtime is the summation of several steps but is dominated by the second step13
# O(M*N + C*C*M*N + (O(C*C) + O(M*N)) + M*N) -> O(C*C*M*N)14
def compare(a: Layer, b: Layer) -> int:16
Determine if two rectangles overlap.20
0 if there is no overlap or an order cannot be determined (the overlap contains no elements of a or b)23
val_a, (a_ul, a_lr) = a24
val_b, (b_ul, b_lr) = b26
# Get overlap rectangle28
(max(a_ul[0], b_ul[0]), max(a_ul[1], b_ul[1])),29
(min(a_lr[0], b_lr[0]), min(a_lr[1], b_lr[1])),32
# If either dimension is non-positive, there is no overlap33
if lr[0] - ul[0] <= 0 or lr[1] - ul[1] <= 0:36
# Find the first element matching a or b in the overlap rectangle.37
# We'll consider that the "over" value.38
for r in range(ul[0], lr[0]):39
for c in range(ul[1], lr[1]):40
if targetGrid[r][c] == val_b:42
elif targetGrid[r][c] == val_a:44
# We could find no values from a or b in the overlap.45
# The result is indeterminate.48
# Generate the enclosing rectangles for each visible color (ie. layers).50
rects: Dict[Color, Rect] = defaultdict(lambda: ([100, 100], [0, 0]))51
for r, row in enumerate(targetGrid):52
for c, val in enumerate(row):55
(min(ul[0], r), min(ul[1], c)),56
(max(lr[0], r + 1), max(lr[1], c + 1)),59
# Compare every pair of layers.60
# If overlap is detected, record that the "upper" rectangle depends on the "lower" one.61
# O(C*C*M*N) # Number of colors62
layers: List[Layer] = list(rects.items())63
graph: Dict[Layer, Set[Layer]] = {layer: set() for layer in layers}64
for i, a in enumerate(layers):65
for b in layers[i + 1 :]:66
if (cmp := compare(a, b)) < 0:71
# Use topological sort on the graph to reproduce the printing order (in the absence72
# of cycles) and print our own grid.73
# O(C*C) + O(M*N) // O(C*C) is derived from topological sort O(V+E)75
grid = [[0] * len(targetGrid[0]) for _ in targetGrid]76
for color, (ul, lr) in TopologicalSorter(graph).static_order():77
for r in range(ul[0], lr[0]):78
for c in range(ul[1], lr[1]):85
return grid == targetGrid