2023-12-27 17:47:38 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2023-12-28 19:48:29 +00:00
|
|
|
"math/rand"
|
|
|
|
"strings"
|
2023-12-27 17:47:38 +00:00
|
|
|
|
|
|
|
h "git.bullercodeworks.com/brian/adventofcode/helpers"
|
|
|
|
)
|
|
|
|
|
2023-12-28 19:48:29 +00:00
|
|
|
type component struct {
|
|
|
|
id string
|
|
|
|
connections map[string]component
|
|
|
|
conns map[string]bool
|
|
|
|
}
|
|
|
|
|
|
|
|
type components struct {
|
|
|
|
all map[string]component
|
|
|
|
edges []connections
|
|
|
|
}
|
|
|
|
|
|
|
|
type connections struct {
|
|
|
|
from, to string
|
|
|
|
track []string
|
|
|
|
}
|
|
|
|
|
2023-12-27 17:47:38 +00:00
|
|
|
func main() {
|
2023-12-28 19:48:29 +00:00
|
|
|
lines := h.StdinToStringSlice()
|
|
|
|
|
|
|
|
var comps components
|
|
|
|
var edges []connections
|
|
|
|
var counter int
|
|
|
|
var tries int
|
|
|
|
for tries < 100 {
|
|
|
|
counter = 0
|
|
|
|
for {
|
|
|
|
counter++
|
|
|
|
compsS := components{all: make(map[string]component)}
|
|
|
|
for _, line := range lines {
|
|
|
|
connect(&compsS, line)
|
|
|
|
}
|
|
|
|
cc := minCut(compsS)
|
|
|
|
cuts := len(cc.edges)
|
|
|
|
edges = cc.edges
|
|
|
|
comps = compsS
|
|
|
|
if cuts == 3 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if counter >= 100 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tries++
|
|
|
|
if counter < 100 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
total := cutInGroups(&comps, edges)
|
|
|
|
fmt.Println(total)
|
|
|
|
}
|
|
|
|
|
|
|
|
func connect(comps *components, line string) {
|
|
|
|
split := strings.Split(line, ": ")
|
|
|
|
nodeName := split[0]
|
|
|
|
connectionsNames := strings.Split(split[1], " ")
|
|
|
|
|
|
|
|
node := component{id: nodeName, connections: make(map[string]component), conns: make(map[string]bool)}
|
|
|
|
if _, ok := comps.all[nodeName]; !ok {
|
|
|
|
comps.all[nodeName] = node
|
|
|
|
}
|
|
|
|
for _, neighborName := range connectionsNames {
|
|
|
|
neighbor := component{id: neighborName, connections: make(map[string]component), conns: make(map[string]bool)}
|
|
|
|
if _, ok := comps.all[neighborName]; !ok {
|
|
|
|
comps.all[neighborName] = neighbor
|
|
|
|
}
|
|
|
|
comps.all[nodeName].connections[neighborName] = neighbor
|
|
|
|
comps.all[nodeName].conns[neighborName] = true
|
|
|
|
comps.all[neighborName].connections[nodeName] = node
|
|
|
|
comps.all[neighborName].conns[nodeName] = true
|
|
|
|
comps.edges = append(comps.edges, connections{
|
|
|
|
from: nodeName,
|
|
|
|
to: neighborName,
|
|
|
|
track: []string{nodeName, neighborName},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func minCut(comps components) components {
|
|
|
|
cc := components{
|
|
|
|
all: make(map[string]component),
|
|
|
|
edges: comps.edges,
|
|
|
|
}
|
|
|
|
for key, val := range comps.all {
|
|
|
|
cc.all[key] = val
|
|
|
|
}
|
|
|
|
|
|
|
|
for len(cc.all) > 2 {
|
|
|
|
u, v := randomComps(&cc)
|
|
|
|
contract(&cc, u, v)
|
|
|
|
}
|
|
|
|
|
|
|
|
return cc
|
|
|
|
}
|
|
|
|
|
|
|
|
func randomComps(comps *components) (component, component) {
|
|
|
|
randomN := rand.Intn(len(comps.edges))
|
|
|
|
edge := comps.edges[randomN]
|
|
|
|
comps.edges = append(comps.edges[:randomN], comps.edges[randomN+1:]...)
|
|
|
|
randCorn := rand.Intn(2)
|
|
|
|
var nameU, nameV string
|
|
|
|
if randCorn == 0 {
|
|
|
|
nameU = edge.from
|
|
|
|
nameV = edge.to
|
|
|
|
} else {
|
|
|
|
nameU = edge.to
|
|
|
|
nameV = edge.from
|
|
|
|
}
|
|
|
|
u, v := comps.all[nameU], comps.all[nameV]
|
|
|
|
return u, v
|
|
|
|
}
|
|
|
|
|
|
|
|
func contract(comps *components, u component, v component) {
|
|
|
|
delete(comps.all, v.id)
|
|
|
|
delete(comps.all[u.id].connections, v.id)
|
|
|
|
for _, node := range comps.all {
|
|
|
|
if _, ok := node.connections[v.id]; ok {
|
|
|
|
delete(node.connections, v.id)
|
|
|
|
node.connections[u.id] = u
|
|
|
|
comps.all[u.id].connections[node.id] = node
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for i := 0; i < len(comps.edges); {
|
|
|
|
if comps.edges[i].from == v.id {
|
|
|
|
comps.edges[i].from = u.id
|
|
|
|
}
|
|
|
|
if comps.edges[i].to == v.id {
|
|
|
|
comps.edges[i].to = u.id
|
|
|
|
}
|
|
|
|
if comps.edges[i].from == comps.edges[i].to {
|
|
|
|
comps.edges = append(comps.edges[:i], comps.edges[i+1:]...)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
i++
|
|
|
|
}
|
2023-12-27 17:47:38 +00:00
|
|
|
}
|
|
|
|
|
2023-12-28 19:48:29 +00:00
|
|
|
func cutInGroups(comps *components, edges []connections) int {
|
|
|
|
var final1, final2 string
|
|
|
|
for i, edge := range edges {
|
|
|
|
cut1 := edge.track[0]
|
|
|
|
cut2 := edge.track[1]
|
|
|
|
delete(comps.all[cut1].conns, cut2)
|
|
|
|
delete(comps.all[cut2].conns, cut1)
|
|
|
|
if i == len(edges)-1 {
|
|
|
|
final1 = cut1
|
|
|
|
final2 = cut2
|
|
|
|
}
|
|
|
|
}
|
|
|
|
group1 := countN(comps, final1)
|
|
|
|
group2 := countN(comps, final2)
|
|
|
|
return group1 * group2
|
2023-12-27 17:47:38 +00:00
|
|
|
}
|
|
|
|
|
2023-12-28 19:48:29 +00:00
|
|
|
func countN(comps *components, start string) int {
|
|
|
|
visited := make(map[string]bool)
|
|
|
|
counter := 0
|
|
|
|
entry := start
|
|
|
|
path := []string{entry}
|
|
|
|
for len(path) > 0 {
|
|
|
|
currentPath := path[0]
|
|
|
|
path = path[1:]
|
|
|
|
if _, ok := visited[currentPath]; ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
counter++
|
|
|
|
currentcomponent := comps.all[currentPath]
|
|
|
|
visited[currentPath] = true
|
|
|
|
for key := range currentcomponent.conns {
|
|
|
|
if _, ok := visited[key]; ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
path = append(path, key)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return counter
|
2023-12-27 17:47:38 +00:00
|
|
|
}
|