mirror of
https://github.com/Threnklyn/advent-of-code-go.git
synced 2026-05-18 19:13:27 +02:00
2015-day09: traveling salesman/santa
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/alexchao26/advent-of-code-go/mathutil"
|
||||
|
||||
"github.com/alexchao26/advent-of-code-go/cast"
|
||||
"github.com/alexchao26/advent-of-code-go/util"
|
||||
)
|
||||
|
||||
func main() {
|
||||
min, max := travelingSalesman(util.ReadFile("./input.txt"))
|
||||
fmt.Printf("Part1: %d\nPart2: %d\n", min, max)
|
||||
}
|
||||
|
||||
func travelingSalesman(input string) (int, int) {
|
||||
graph := newGraphFromInput(input)
|
||||
|
||||
min := math.MaxInt32
|
||||
max := 0
|
||||
for k := range graph {
|
||||
dfsMin, dfsMax := dfsTotalDistance(graph, k, map[string]bool{k: true})
|
||||
min = mathutil.MinInt(min, dfsMin)
|
||||
max = mathutil.MaxInt(max, dfsMax)
|
||||
}
|
||||
|
||||
return min, max
|
||||
}
|
||||
|
||||
func dfsTotalDistance(graph mapGraph, entry string, visited map[string]bool) (min, max int) {
|
||||
// if all nodes have been visited, return a zero length
|
||||
if len(visited) == len(graph) {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
minDistance := math.MaxInt32
|
||||
maxDistance := 0
|
||||
|
||||
for k := range graph {
|
||||
if !visited[k] {
|
||||
visited[k] = true
|
||||
|
||||
weight := graph[entry][k]
|
||||
minRecurse, maxRecurse := dfsTotalDistance(graph, k, visited)
|
||||
minDistance = mathutil.MinInt(minDistance, weight+minRecurse)
|
||||
maxDistance = mathutil.MaxInt(maxDistance, weight+maxRecurse)
|
||||
|
||||
// backtrack
|
||||
// delete to so length of visited is accurate
|
||||
delete(visited, k)
|
||||
}
|
||||
}
|
||||
|
||||
return minDistance, maxDistance
|
||||
}
|
||||
|
||||
type mapGraph map[string]map[string]int
|
||||
|
||||
func newGraphFromInput(input string) (graph mapGraph) {
|
||||
graph = make(mapGraph)
|
||||
for _, line := range strings.Split(input, "\n") {
|
||||
parts := strings.Split(line, " ")
|
||||
start, end := parts[0], parts[2]
|
||||
weight := cast.ToInt(parts[4])
|
||||
|
||||
// ensure nested map exists
|
||||
if graph[start] == nil {
|
||||
graph[start] = make(map[string]int)
|
||||
}
|
||||
if graph[end] == nil {
|
||||
graph[end] = make(map[string]int)
|
||||
}
|
||||
|
||||
// set weight in both directions
|
||||
graph[start][end] = weight
|
||||
graph[end][start] = weight
|
||||
}
|
||||
return graph
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/alexchao26/advent-of-code-go/util"
|
||||
)
|
||||
|
||||
func Test_travelingSalesman(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
wantPart1 int
|
||||
wantPart2 int
|
||||
}{
|
||||
{"actual", util.ReadFile("input.txt"), 117, 909},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got1, got2 := travelingSalesman(tt.input)
|
||||
if got1 != tt.wantPart1 {
|
||||
t.Errorf("travelingSalesman() part1 = %v, want %v", got1, tt.wantPart1)
|
||||
}
|
||||
if got2 != tt.wantPart2 {
|
||||
t.Errorf("travelingSalesman() part2 = %v, want %v", got2, tt.wantPart2)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user