From b9a74cf122abd53c13bc30e046641ea00b4ee5e1 Mon Sep 17 00:00:00 2001 From: alexchao26 Date: Thu, 10 Dec 2020 00:46:21 -0500 Subject: [PATCH] 2020 day 10 - memoized or dynamic programming to calculate paths/possibilities, ~830 --- 2020/day10/main.go | 93 ++++++++++++++++++++++++++++++++++++++--- 2020/day10/main_test.go | 79 +++++++++++++++++++++++++++++++--- 2 files changed, 161 insertions(+), 11 deletions(-) diff --git a/2020/day10/main.go b/2020/day10/main.go index 8dcc68c..7c5b027 100644 --- a/2020/day10/main.go +++ b/2020/day10/main.go @@ -3,8 +3,10 @@ package main import ( "flag" "fmt" + "sort" "strings" + "github.com/alexchao26/advent-of-code-go/algo" "github.com/alexchao26/advent-of-code-go/util" ) @@ -26,17 +28,34 @@ func main() { } func part1(input string) int { - parsed := parseInput(input) - _ = parsed + nums := parseInput(input) + nums = append(nums, util.MaxInts(nums...)+3) + sort.Ints(nums) - return 0 + var oneDiff, threeDiff int + var currentJoltage int + for _, v := range nums { + switch v - currentJoltage { + case 1: // check for 1 diff first, so no adapters are skipped + oneDiff++ + case 3: + threeDiff++ + default: + panic("adpaters not connected by 3 or 1") + } + currentJoltage = v + } + + return oneDiff * threeDiff } func part2(input string) int { - parsed := parseInput(input) - _ = parsed + nums := parseInput(input) + nums = append(nums, util.MaxInts(nums...)+3) + sort.Ints(nums) - return 0 + // return dynamicProgramming(input) + return memoCountPossibilities(nums, 0) } func parseInput(input string) []int { @@ -49,3 +68,65 @@ func parseInput(input string) []int { return ans } + +// storing memo in global state isn't ideal... but it's fastser to code +var memo = map[string]int{} + +func memoCountPossibilities(nums []int, lastJolt int) int { + // if in memo, return that value + str := makeMemoKey(nums, lastJolt) + if v, ok := memo[str]; ok { + return v + } + + // if all adapters used up, return 1 + if len(nums) == 0 { + return 1 + } + + // create a recursive call for each adapter within 3 of the lastJoltage + var count int + for i, v := range nums { + if v-lastJolt <= 3 { + count += memoCountPossibilities(nums[i+1:], v) + } else { // stop counting if the joltage diff is too larger (>3) + break + } + } + + // update memo + memo[str] = count + + return count +} +func makeMemoKey(nums []int, lastJolt int) string { + ans := algo.IntToStr(lastJolt) + "x" + for _, v := range nums { + ans += algo.IntToStr(v) + } + return ans +} + +func dynamicProgramming(input string) int { + nums := parseInput(input) + nums = append(nums, util.MaxInts(nums...)+3, 0) + sort.Ints(nums) + + // initialize table with "1 way" to get to zero jolts + table := make([]int, len(nums)) + table[0] = 1 + + for i := 1; i < len(nums); i++ { + currentJolts := nums[i] + for j := i - 1; j >= 0; j-- { + // add the ways to get to currentJolts that are within 3 jolts + if currentJolts-nums[j] <= 3 { + table[i] += table[j] + } else { + break + } + } + } + + return table[len(table)-1] +} diff --git a/2020/day10/main_test.go b/2020/day10/main_test.go index ee82d4e..5deeb2e 100644 --- a/2020/day10/main_test.go +++ b/2020/day10/main_test.go @@ -1,14 +1,63 @@ package main -import "testing" +import ( + "testing" + + "github.com/alexchao26/advent-of-code-go/util" +) + +var exampleInputs = []string{`16 +10 +15 +5 +1 +11 +7 +19 +6 +12 +4`, + `28 +33 +18 +42 +31 +14 +46 +20 +48 +47 +24 +23 +49 +45 +19 +38 +39 +11 +1 +32 +25 +35 +8 +17 +7 +9 +4 +2 +34 +10 +3`, +} var tests1 = []struct { name string want int input string - // add extra args if needed }{ - // {"actual", ACTUAL_ANSWER, util.ReadFile("input.txt")}, + {"example 1", 35, exampleInputs[0]}, + {"example 2", 220, exampleInputs[1]}, + {"actual", 2176, util.ReadFile("input.txt")}, } func TestPart1(t *testing.T) { @@ -25,9 +74,10 @@ var tests2 = []struct { name string want int input string - // add extra args if needed }{ - // {"actual", ACTUAL_ANSWER, util.ReadFile("input.txt")}, + {"example 1", 8, exampleInputs[0]}, + {"example 2", 19208, exampleInputs[1]}, + {"actual", 18512297918464, util.ReadFile("input.txt")}, } func TestPart2(t *testing.T) { @@ -39,3 +89,22 @@ func TestPart2(t *testing.T) { }) } } + +func Test_dynamicProgramming(t *testing.T) { + tests := []struct { + name string + want int + input string + }{ + {"example 1", 8, exampleInputs[0]}, + {"example 2", 19208, exampleInputs[1]}, + {"actual", 18512297918464, util.ReadFile("input.txt")}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := dynamicProgramming(tt.input); got != tt.want { + t.Errorf("dynamicProgramming() = %v, want %v", got, tt.want) + } + }) + } +}