-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path23.py
More file actions
91 lines (79 loc) · 3.06 KB
/
23.py
File metadata and controls
91 lines (79 loc) · 3.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import collections
lines = [line.strip() for line in open('23.txt')]
start_row, start_col = start = (0, 1)
end_row, end_col = end = (len(lines)-1, len(lines[0])-2)
nodes = {start, end}
for row, line in enumerate(lines):
for col, val in enumerate(line):
if not (1 <= row < len(lines) - 1 and 1 <= col < len(lines) - 1) or lines[row][col] != '.':
continue
if sum(lines[nr][nc] in '^>v<' for nr, nc in [(row-1, col), (row+1, col), (row, col-1), (row, col+1)]) >= 3:
nodes.add((row, col))
dag = collections.defaultdict(dict)
graph = collections.defaultdict(dict)
for node in nodes:
if node == end:
continue
Q = []
visited = {node}
r, c = node
for nr, nc, direction in [(r-1, c, '^'), (r, c+1, '>'), (r+1, c, 'v'), (r, c-1, '<')]:
if not 0 <= nr < len(lines) or not 0 <= nc < len(lines[0]) or lines[nr][nc] == '#':
continue
Q.append((nr, nc, lines[nr][nc] in direction + '.'))
visited.add((nr, nc))
steps = 0
while Q:
next_Q = []
for r, c, dag_legal in Q:
if (r, c) in nodes:
graph[node][(r, c)] = steps + 1
if dag_legal:
dag[node][(r, c)] = steps + 1
continue
for nr, nc in [(r-1, c), (r+1, c), (r, c-1), (r, c+1)]:
if not 0 <= nr < len(lines) or not 0 <= nc < len(lines[0]) or lines[nr][nc] == '#' or (nr, nc) in visited:
continue
next_Q.append((nr, nc, dag_legal))
visited.add((nr, nc))
steps += 1
Q = next_Q
def solve(G):
nodes = set()
for src, dsts in G.items():
nodes.add(src)
nodes |= set(dsts)
node_ids = {node: i for i, node in enumerate(nodes)}
G = {node_ids[src]: {node_ids[dst]: distance for dst, distance in dsts.items()} for src, dsts in G.items()}
paths = [(node_ids[start], 1<<node_ids[start], 0)]
max_steps = 0
for (src, visited, steps) in paths:
if src == node_ids[end]:
max_steps = max(steps, max_steps)
continue
for dst in G[src]:
if 1<<dst & visited:
continue
paths.append((dst, visited | 1<<dst, steps + G[src][dst]))
return max_steps
# This is completely excessive since part 1 terminates quickly even with a brute
# force approach, but I didn't know this algorithm so I wanted to implement it.
def solve_dag(G):
Gt = collections.defaultdict(dict)
for src, dsts in G.items():
for dst, dist in dsts.items():
Gt[dst][src] = dist
indegree = {src: len(dsts) for src, dsts in Gt.items()}
indegree[start] = 0
todo = [src for src, d in indegree.items() if d == 0]
distance = {start: 0}
for node in todo:
if node != start:
distance[node] = max(distance[src] + cost for src, cost in Gt[node].items())
for dst in G[node]:
indegree[dst] -= 1
if indegree[dst] == 0:
todo.append(dst)
return distance[end]
assert solve_dag(dag) == 2254
assert solve(graph) == 6394