Unlike other people, I don't have a ready set of graph algorithms ready, so I tried a bit to implement Karger, before I went back and simply added -Kneato to my dot graph output and then the edges to cut became very clearly visible. I could have saved a couple of hours.
This commit is contained in:
parent
0acad0121c
commit
73164a0ee3
167
src/aoc2023/Day25.kt
Normal file
167
src/aoc2023/Day25.kt
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
package aoc2023
|
||||||
|
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 25: Snowverload ---
|
||||||
|
Still somehow without snow, you go to the last place you haven't checked: the center of Snow Island, directly below the waterfall.
|
||||||
|
Here, someone has clearly been trying to fix the problem. Scattered everywhere are hundreds of weather machines, almanacs, communication modules, hoof prints, machine parts, mirrors, lenses, and so on.
|
||||||
|
Somehow, everything has been wired together into a massive snow-producing apparatus, but nothing seems to be running. You check a tiny screen on one of the communication modules: Error 2023. It doesn't say what Error 2023 means, but it does have the phone number for a support line printed on it.
|
||||||
|
"Hi, you've reached Weather Machines And So On, Inc. How can I help you?" You explain the situation.
|
||||||
|
"Error 2023, you say? Why, that's a power overload error, of course! It means you have too many components plugged in. Try unplugging some components and--" You explain that there are hundreds of components here and you're in a bit of a hurry.
|
||||||
|
"Well, let's see how bad it is; do you see a big red reset button somewhere? It should be on its own module. If you push it, it probably won't fix anything, but it'll report how overloaded things are." After a minute or two, you find the reset button; it's so big that it takes two hands just to get enough leverage to push it. Its screen then displays:
|
||||||
|
SYSTEM OVERLOAD!
|
||||||
|
|
||||||
|
Connected components would require
|
||||||
|
power equal to at least 100 stars!
|
||||||
|
|
||||||
|
"Wait, how many components did you say are plugged in? With that much equipment, you could produce snow for an entire--" You disconnect the call.
|
||||||
|
You have nowhere near that many stars - you need to find a way to disconnect at least half of the equipment here, but it's already Christmas! You only have time to disconnect three wires.
|
||||||
|
Fortunately, someone left a wiring diagram (your puzzle input) that shows how the components are connected. For example:
|
||||||
|
jqt: rhn xhk nvd
|
||||||
|
rsh: frs pzl lsr
|
||||||
|
xhk: hfx
|
||||||
|
cmg: qnr nvd lhk bvb
|
||||||
|
rhn: xhk bvb hfx
|
||||||
|
bvb: xhk hfx
|
||||||
|
pzl: lsr hfx nvd
|
||||||
|
qnr: nvd
|
||||||
|
ntq: jqt hfx bvb xhk
|
||||||
|
nvd: lhk
|
||||||
|
lsr: lhk
|
||||||
|
rzs: qnr cmg lsr rsh
|
||||||
|
frs: qnr lhk lsr
|
||||||
|
|
||||||
|
Each line shows the name of a component, a colon, and then a list of other components to which that component is connected. Connections aren't directional; abc: xyz and xyz: abc both represent the same configuration. Each connection between two components is represented only once, so some components might only ever appear on the left or right side of a colon.
|
||||||
|
In this example, if you disconnect the wire between hfx/pzl, the wire between bvb/cmg, and the wire between nvd/jqt, you will divide the components into two separate, disconnected groups:
|
||||||
|
|
||||||
|
9 components: cmg, frs, lhk, lsr, nvd, pzl, qnr, rsh, and rzs.
|
||||||
|
6 components: bvb, hfx, jqt, ntq, rhn, and xhk.
|
||||||
|
|
||||||
|
Multiplying the sizes of these groups together produces 54.
|
||||||
|
Find the three wires you need to disconnect in order to divide the components into two separate groups. What do you get if you multiply the sizes of these two groups together?
|
||||||
|
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
jqt: rhn xhk nvd
|
||||||
|
rsh: frs pzl lsr
|
||||||
|
xhk: hfx
|
||||||
|
cmg: qnr nvd lhk bvb
|
||||||
|
rhn: xhk bvb hfx
|
||||||
|
bvb: xhk hfx
|
||||||
|
pzl: lsr hfx nvd
|
||||||
|
qnr: nvd
|
||||||
|
ntq: jqt hfx bvb xhk
|
||||||
|
nvd: lhk
|
||||||
|
lsr: lhk
|
||||||
|
rzs: qnr cmg lsr rsh
|
||||||
|
frs: qnr lhk lsr
|
||||||
|
"""
|
||||||
|
|
||||||
|
data class Node(val id: String, val children: MutableSet<Node> = HashSet(), val orgNodes: MutableSet<Node> = HashSet()) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as Node
|
||||||
|
|
||||||
|
return id == other.id
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return id.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "Node(id='$id', children=${children.joinToString { it.id }})"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun connectedSize(rootNode: Node): Int {
|
||||||
|
val visited = HashSet<Node>()
|
||||||
|
val queue = ArrayDeque<Node>()
|
||||||
|
queue.add(rootNode)
|
||||||
|
while (queue.isNotEmpty()) {
|
||||||
|
val n = queue.removeFirst()
|
||||||
|
visited.add(n)
|
||||||
|
n.children.forEach { if (!visited.contains(it)) queue.add(it) }
|
||||||
|
}
|
||||||
|
return visited.size
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part1(input: List<String>): Int {
|
||||||
|
val nodeMap = HashMap<String, Node>()
|
||||||
|
for (i in input) {
|
||||||
|
val (from, to) = i.split(": ")
|
||||||
|
val tos = to.split(" ")
|
||||||
|
val fromNode = nodeMap.computeIfAbsent(from) { Node(from) }
|
||||||
|
val toNodes = tos.map { nodeMap.computeIfAbsent(it) { Node(it) } }
|
||||||
|
fromNode.children.addAll(toNodes)
|
||||||
|
toNodes.forEach { it.children.add(fromNode) }
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeMap["vps"]!!.children.remove(nodeMap["pzc"])
|
||||||
|
nodeMap["pzc"]!!.children.remove(nodeMap["vps"])
|
||||||
|
nodeMap["xvk"]!!.children.remove(nodeMap["sgc"])
|
||||||
|
nodeMap["sgc"]!!.children.remove(nodeMap["xvk"])
|
||||||
|
nodeMap["dph"]!!.children.remove(nodeMap["cvx"])
|
||||||
|
nodeMap["cvx"]!!.children.remove(nodeMap["dph"])
|
||||||
|
|
||||||
|
val res = connectedSize(nodeMap["vps"]!!) * connectedSize(nodeMap["pzc"]!!)
|
||||||
|
|
||||||
|
// val edges = nodeMap.values.map { s -> s.children.map { t -> s to t } }.flatten()
|
||||||
|
//
|
||||||
|
// val nodes = nodeMap.values.toMutableList()
|
||||||
|
// while (nodes.size > 2) {
|
||||||
|
// val n1 = nodes[(Math.random() * nodes.size).toInt().coerceAtMost(nodes.lastIndex)]
|
||||||
|
// val n2 = n1.children.keys.toList()[(Math.random() * n1.children.size).toInt().coerceAtMost(n1.children.size - 1)]
|
||||||
|
// val superNode = Node(n1.id + "-" + n2.id)
|
||||||
|
// if (n1.orgNodes.isEmpty()) {
|
||||||
|
// superNode.orgNodes.add(Node(n1.id, children = HashMap(n1.children)))
|
||||||
|
// } else {
|
||||||
|
// superNode.orgNodes.addAll(n1.orgNodes)
|
||||||
|
// }
|
||||||
|
// if (n2.orgNodes.isEmpty()) {
|
||||||
|
// superNode.orgNodes.add(Node(n2.id, children = HashMap(n2.children)))
|
||||||
|
// } else {
|
||||||
|
// superNode.orgNodes.addAll(n2.orgNodes)
|
||||||
|
// }
|
||||||
|
// n1.children.filter { it.key != n2 }
|
||||||
|
// .forEach { }
|
||||||
|
// superNode.children.addAll(n1.children.filter { it != n2 })
|
||||||
|
// superNode.children.addAll(n2.children.filter { it != n1 })
|
||||||
|
//
|
||||||
|
// superNode.children.forEach {
|
||||||
|
// it.children.remove(n1)
|
||||||
|
// it.children.remove(n2)
|
||||||
|
// it.children.add(superNode)
|
||||||
|
// }
|
||||||
|
// nodes.remove(n1)
|
||||||
|
// nodes.remove(n2)
|
||||||
|
// nodes.add(superNode)
|
||||||
|
// }
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2023/Day25_test")
|
||||||
|
//val testInputPart1Result = part1(testInput)
|
||||||
|
//println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
//check(testInputPart1Result == 0)
|
||||||
|
check(testInputPart2Result == 0)
|
||||||
|
|
||||||
|
val input = readInput("aoc2023/Day25")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
BIN
src/aoc2023/Day25.png
Normal file
BIN
src/aoc2023/Day25.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 MiB |
Loading…
Reference in New Issue
Block a user