Skip to content

Commit 88ef403

Browse files
committed
LC 980
1 parent 7d27cb0 commit 88ef403

File tree

1 file changed

+126
-0
lines changed

1 file changed

+126
-0
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
"""
2+
Visit every 0 cell once: tree? Can't have cycles.
3+
Hamiltonian path: visits each vertex exactly once; needs to start at 1, end at 0, and not visit -1.
4+
Start at 2? does that help at all?
5+
max cells - 400 (20 x 20); n^2 is 160000, so this is viable
6+
7+
=== Brute force / DFS approach ===
8+
- Count number of -1 beforehand
9+
- Do regular DFS starting at 1, except can't visit -1, can't revisit, and can't visit 2 unless n*m-(count of -1) cells have been visited
10+
- Return 1 if we reach 2, return 0 otherwise
11+
- Can we do this but prune?
12+
13+
=== DP approach ===
14+
- "Hamiltonian Zero Path" (HZP): path in grid starting at start, reaching target cell, crossing every 0 only once.
15+
- Num of HZP(1,2): sum of HZP(1, neighbor) for each of 2's neighbors (each is unique since they end at a different neighbor)
16+
- This doesn't hold for a 2x2 -> 3x3 grid; adding cells changes whether a previously reachable cell is reachable or not.
17+
- 1x1 is not valid input. 2x1, 1x2 are trivial; 1 path.
18+
- 2x2 has one allowed for only one graph:
19+
[1, 2]
20+
[0, 0] (D-R-U)
21+
- Any other arrangement (not counting rotations) has no solution
22+
- Any 2x2 with nonzero -1s has no solution
23+
24+
This has 4 ways to make acyclic paths of len 12.
25+
[1, 0, 0, 0]
26+
[0, 0, 0, 0]
27+
[0, 0, 0, 2]
28+
29+
Grid Recurrence for HZPs (X == -1)
30+
[1, 0, 0, 0] -> [1, 0, 0, 0]
31+
[0, 0, 0, 0] [0, 0, 0, 2] 2 ways for acyclic paths of len 11
32+
[0, 0, 0, 2] [0, 0, 0, X]
33+
+
34+
[1, 0, 0, 0]
35+
[0, 0, 0, 0] 2 ways for acyclic paths of len 11
36+
[0, 0, 2, X]
37+
38+
Does this strategy evaluate paths it doesn't need to? Doesn't seem different
39+
from just trying a quadratic DFS from 1.
40+
- Don't need to repeatedly try to find (y,x,k), i.e. count of valid k-len paths to grid[y,x]
41+
- Do we need to try (y,x,k) and (y,x,n) if n != k? some paths may take (y,x) as the nth node, others as the kth node.
42+
43+
Trying smaller example:
44+
This has 1 way to make acyclic path of len 5.
45+
[1, 0, 0]
46+
[0, 0, 2]
47+
48+
49+
Grid Recurrence for HZPs (X == -1)
50+
[1, 0, 0] -> [1, 0, 2] 1 path
51+
[0, 0, 2] [0, 0, x]
52+
+
53+
[1, 0, 0]
54+
[0, 2, X] 0 paths
55+
56+
[1, 2, X] -> .... (etc)
57+
[0, 0, X]
58+
59+
"""
60+
from typing import List
61+
62+
63+
def is_valid(grid, y, x):
64+
return (0 <= y < len(grid)) and (0 <= x < len(grid[0]))
65+
66+
67+
def get_neighbors(grid, y, x):
68+
neighbors = []
69+
for dy, dx in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
70+
neighbors.append((y+dy, x+dx)) if is_valid(grid, y+dy, x+dx) else None
71+
return neighbors
72+
73+
74+
def dfs(grid, y, x, path_len, visited):
75+
cell = grid[y][x]
76+
if (y, x) in visited or cell == -1:
77+
return 0
78+
if cell == 2:
79+
return 1 if path_len == 0 else 0
80+
81+
paths = 0
82+
visited.add((y, x))
83+
for y_n, x_n in get_neighbors(grid, y, x):
84+
paths += dfs(grid, y_n, x_n, path_len-1, visited)
85+
visited.remove((y, x))
86+
return paths
87+
88+
89+
class Solution:
90+
def uniquePathsIII(self, grid: List[List[int]]) -> int:
91+
start_y = start_x = 0
92+
required_cells = len(grid) * len(grid[0])
93+
for y, row in enumerate(grid):
94+
for x, cell in enumerate(row):
95+
if cell == -1:
96+
required_cells -= 1
97+
elif cell == 1:
98+
start_y, start_x = y, x
99+
required_cells -= 1
100+
return dfs(grid, start_y, start_x, required_cells, set())
101+
102+
103+
s = Solution()
104+
cases = [
105+
([
106+
[1, 0, 0, 0],
107+
[0, 0, 0, 0],
108+
[0, 0, 2, -1]], 2),
109+
([
110+
[1, 0, 0, 0],
111+
[0, 0, 0, 0],
112+
[0, 0, 0, 2]], 4),
113+
([
114+
[0, 1],
115+
[2, 0]], 0),
116+
]
117+
for grid, expected in cases:
118+
actual = s.uniquePathsIII(grid)
119+
try:
120+
assert actual == expected
121+
except AssertionError:
122+
print("=="*10)
123+
for row in grid:
124+
print(row)
125+
print(f"{expected} != {actual}")
126+
raise

0 commit comments

Comments
 (0)