-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Not an ideal approach, but finds the right edges to remove on the first try ~90% of the time Most the runtime is rand.Perm - could probably just start at a random index into the list instead of generating a full permutation
- Loading branch information
Showing
4 changed files
with
212 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
package day25 | ||
|
||
import ( | ||
_ "embed" | ||
"github.com/ictrobot/aoc/internal/util/parse" | ||
"math/rand" | ||
"strings" | ||
) | ||
|
||
//go:embed example | ||
var Example string | ||
|
||
type Day25 struct { | ||
edges [][]int | ||
} | ||
|
||
func (d *Day25) Parse(input string) { | ||
lines := parse.Grid(parse.Lines, parse.Whitespace, strings.ReplaceAll(input, ":", "")) | ||
|
||
names := make(map[string]int) | ||
for _, line := range lines { | ||
for _, name := range line { | ||
if _, exists := names[name]; !exists { | ||
names[name] = len(names) | ||
} | ||
} | ||
} | ||
|
||
d.edges = make([][]int, len(names)) | ||
for _, line := range lines { | ||
lhs := names[line[0]] | ||
for _, name := range line[1:] { | ||
rhs := names[name] | ||
d.edges[lhs] = append(d.edges[lhs], rhs) | ||
d.edges[rhs] = append(d.edges[rhs], lhs) | ||
} | ||
} | ||
} | ||
|
||
func (d *Day25) ParseExample() { | ||
d.Parse(Example) | ||
} | ||
|
||
func (d *Day25) Part1() any { | ||
for { | ||
removedEdges := d.edgesToRemove() | ||
count1, count2, ok := d.checkSplit(removedEdges) | ||
if ok { | ||
return count1 * count2 | ||
} | ||
} | ||
} | ||
|
||
func (d *Day25) Part2() any { | ||
// no part 2 | ||
return "🎄" | ||
} | ||
|
||
func (d *Day25) edgesToRemove() (removedEdges [3][2]int) { | ||
edgeCount := make(map[[2]int]int) | ||
visited := make([]bool, len(d.edges)) | ||
|
||
var dfs func(int, int) | ||
dfs = func(v, removed int) { | ||
visited[v] = true | ||
|
||
// rand.Perm is needed to avoid always visiting the same edge first | ||
// from a given vertex which leads to almost always visiting nodes | ||
// in the same order | ||
neighbours: | ||
for _, p := range rand.Perm(len(d.edges[v])) { | ||
neighbor := d.edges[v][p] | ||
if visited[neighbor] { | ||
continue | ||
} | ||
|
||
edge := [2]int{min(v, neighbor), max(v, neighbor)} | ||
for _, e := range removedEdges[:removed] { | ||
if e == edge { | ||
continue neighbours | ||
} | ||
} | ||
edgeCount[edge]++ | ||
|
||
dfs(neighbor, removed) | ||
} | ||
} | ||
|
||
for i := 0; i < 3; i++ { | ||
clear(edgeCount) | ||
|
||
for source := range d.edges { | ||
clear(visited) | ||
dfs(source, i) | ||
} | ||
|
||
var maxCount int | ||
for e, c := range edgeCount { | ||
if c > maxCount { | ||
maxCount = c | ||
removedEdges[i] = e | ||
} | ||
} | ||
} | ||
|
||
return | ||
} | ||
|
||
func (d *Day25) checkSplit(removedEdges [3][2]int) (int, int, bool) { | ||
visited := make([]bool, len(d.edges)) | ||
|
||
var countReachable func(int) int | ||
countReachable = func(v int) int { | ||
visited[v] = true | ||
count := 1 | ||
|
||
for _, neighbor := range d.edges[v] { | ||
if visited[neighbor] { | ||
continue | ||
} | ||
|
||
edge := [2]int{min(v, neighbor), max(v, neighbor)} | ||
if removedEdges[0] == edge || removedEdges[1] == edge || removedEdges[2] == edge { | ||
continue | ||
} | ||
|
||
count += countReachable(neighbor) | ||
} | ||
|
||
return count | ||
} | ||
|
||
count1 := countReachable(0) | ||
|
||
unvisited := -1 | ||
for i, value := range visited { | ||
if !value { | ||
unvisited = i | ||
break | ||
} | ||
} | ||
if unvisited == -1 { | ||
return 0, 0, false | ||
} | ||
|
||
count2 := countReachable(unvisited) | ||
|
||
if count1+count2 < len(d.edges) { | ||
return 0, 0, false | ||
} | ||
return count1, count2, true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package day25 | ||
|
||
import ( | ||
"github.com/stretchr/testify/assert" | ||
"testing" | ||
) | ||
|
||
const Part1 = 54 | ||
|
||
func TestDay25_ParseExample(t *testing.T) { | ||
d1 := Day25{} | ||
d1.ParseExample() | ||
|
||
d2 := Day25{} | ||
d2.ParseExample() | ||
d2.ParseExample() | ||
|
||
assert.Equal(t, d1, d2, "should be idempotent") | ||
} | ||
|
||
func BenchmarkDay25_ParseExample(b *testing.B) { | ||
d := Day25{} | ||
for i := 0; i < b.N; i++ { | ||
d.ParseExample() | ||
} | ||
} | ||
|
||
func TestDay25_Part1(t *testing.T) { | ||
d := Day25{} | ||
d.ParseExample() | ||
|
||
assert.EqualValues(t, Part1, d.Part1()) | ||
} | ||
|
||
func BenchmarkDay25_Part1(b *testing.B) { | ||
d := Day25{} | ||
d.ParseExample() | ||
|
||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
assert.EqualValues(b, Part1, d.Part1()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
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 |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.