From 0f454c5d1fa55f7095e6f8dfd8920e3b855a1b69 Mon Sep 17 00:00:00 2001 From: Jeremy Kaplan Date: Mon, 6 Dec 2021 21:42:15 -0800 Subject: [PATCH] 2021 Day 7 --- 2021/aoc/aoc.go | 7 +++ 2021/day7/main.go | 114 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 2021/day7/main.go diff --git a/2021/aoc/aoc.go b/2021/aoc/aoc.go index fc9e325..fec2efe 100644 --- a/2021/aoc/aoc.go +++ b/2021/aoc/aoc.go @@ -84,3 +84,10 @@ func Cut(s, sep string) (before, after string) { } panic("Separator was not found in string") } + +func Ints(text string) (ns []int) { + for _, n := range strings.Split(text, ",") { + ns = append(ns, MustInt(n)) + } + return +} diff --git a/2021/day7/main.go b/2021/day7/main.go new file mode 100644 index 0000000..bf81dd3 --- /dev/null +++ b/2021/day7/main.go @@ -0,0 +1,114 @@ +package main + +import ( + "fmt" + + "github.com/jdkaplan/advent-of-code/aoc" +) + +func main() { + text := aoc.Input().ReadFile("day7.txt") + fmt.Println(part1(text)) + fmt.Println(part2(text)) +} + +func part1(text string) int { + ps := aoc.Ints(text) + argmin := ps[0] + min := cost1(argmin, ps) + for _, p := range ps[1:] { + c := cost1(p, ps) + if c < min { + argmin, min = p, c + } + } + fmt.Println("Pos:", argmin) + return min +} + +func part2(text string) int { + ps := aoc.Ints(text) + lo, hi := minmax(ps) + cost, pos := bisect(func(p int) int { return cost2(p, ps) }, lo, hi) + fmt.Println("Pos:", pos) + return cost +} + +func cost1(x int, ns []int) (cost int) { + for _, n := range ns { + cost += abs(n - x) + } + return +} + +func cost2(x int, ns []int) (cost int) { + for _, n := range ns { + d := abs(n - x) + cost += tri(d) + } + return +} + +func abs(n int) int { + if n < 0 { + return -n + } + return n +} + +func tri(n int) int { + return n * (n + 1) / 2 +} + +func bisect(f func(int) int, lo, hi int) (min int, argmin int) { + if lo == hi { + return f(lo), lo + } + if hi-lo == 1 { + flo, fhi := f(lo), f(hi) + if flo < fhi { + return flo, lo + } + return fhi, hi + } + md := (hi + lo) / 2 + switch md { + case lo: + return f(lo), lo + case hi: + return f(hi), hi + default: + s := slope(f, md) + if s == 0 { + return f(md), md + } + if s < 0 { + return bisect(f, md, hi) + } + return bisect(f, lo, md) + } +} + +func slope(f func(int) int, n int) int { + var ( + lo = n - 1 + hi = n + 1 + flo = f(lo) + fhi = f(hi) + ) + return fhi - flo +} + +func minmax(ns []int) (min int, max int) { + min = ns[0] + max = ns[0] + for _, n := range ns[1:] { + if n < min { + min = n + } + if n > max { + max = n + } + } + return +}