-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path16.swift
113 lines (98 loc) · 4.01 KB
/
16.swift
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// Based on https://github.com/gereons/AoC2022/blob/main/Sources/Day16/Day16.swift
import Foundation
guard let input = try? String(contentsOf: URL(fileURLWithPath: "16.txt")) else {
fatalError("File error")
}
extension Set {
public func combinations() -> [Set<Element>] {
var result = [Set<Element>]()
for element in self {
let oneElementCombo = Set([element])
for i in 0..<result.count {
result.append(oneElementCombo.union(result[i]))
}
result.append(oneElementCombo)
}
return result
}
}
struct Valve {
let name: String
let flow: Int
let leads: [String]
init(_ description: String) {
let parts = description.components(separatedBy: ";")
let firstParts = parts[0].components(separatedBy: CharacterSet.init(charactersIn: " ="))
self.name = firstParts[1]
self.flow = Int(firstParts.last!)!
self.leads = Array(parts[1].components(separatedBy: .alphanumerics.inverted).filter { !$0.isEmpty }.dropFirst(4))
}
}
class Graph {
var valves: [String: Valve]
init(_ valves: [Valve]) {
self.valves = valves.reduce(into: [:], { $0[$1.name] = $1 })
}
func distances() -> [String: [String: Int]] {
let flowValves = valves.values.filter { $0.name == "AA" || $0.flow > 0 }.map { $0.name }
let result = flowValves.map { valve in
var distances = [valve: 0]
var queue = [valve]
while !queue.isEmpty {
let current = queue.removeFirst()
for next in valves[current]!.leads {
let newDistance = distances[current]! + 1
if newDistance < distances[next, default: Int.max] {
distances[next] = newDistance
queue.append(next)
}
}
}
distances = distances.filter { valves[$0.key]!.flow > 0 }
return (valve, distances)
}
return Dictionary(uniqueKeysWithValues: result)
}
func searchPaths(from valve: String,
timeAllowed: Int,
visited: Set<String> = [],
distances: [String: [String: Int]],
timeTaken: Int = 0,
totalFlow: Int = 0
) -> Int {
let next = distances[valve]!
.map { ($0, $1) }
.filter { valve, _ in !visited.contains(valve) }
.filter { _, distance in timeTaken + distance + 1 < timeAllowed }
var maxFlow = Int.min
for (nextValve, distance) in next {
let flow = searchPaths(from: nextValve,
timeAllowed: timeAllowed,
visited: visited.union(Set([nextValve])),
distances: distances,
timeTaken: timeTaken + distance + 1,
totalFlow: totalFlow + ((timeAllowed - timeTaken - distance - 1) * valves[nextValve]!.flow)
)
maxFlow = max(maxFlow, flow)
}
return maxFlow != Int.min ? maxFlow : totalFlow
}
}
let lines = input.components(separatedBy: .newlines)
let graph = Graph(lines.map { Valve($0) })
// part 1
let distances = graph.distances()
let answer1 = graph.searchPaths(from: "AA", timeAllowed: 30, distances: distances)
print(answer1)
assert(answer1 == 1701)
// part 2
let valves = Set(distances.keys.filter { $0 != "AA" })
var answer2 = Int.min
let halfSizedCombinations = valves.combinations().filter { $0.count == distances.count / 2 }
for halfOfValves in halfSizedCombinations {
let myPart = graph.searchPaths(from: "AA", timeAllowed: 26, visited: Set(halfOfValves), distances: distances)
let elephant = graph.searchPaths(from: "AA", timeAllowed: 26, visited: valves.subtracting(Set(halfOfValves)), distances: distances)
answer2 = max(answer2, myPart + elephant)
}
print(answer2)
assert(answer2 == 2455)