generated from devries/aoc_template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
solution.go
130 lines (109 loc) · 2.69 KB
/
solution.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package day12p1
import (
"fmt"
"io"
"strconv"
"strings"
"aoc/utils"
)
func Solve(r io.Reader) any {
lines := utils.ReadLines(r)
sum := int64(0)
for _, ln := range lines {
if utils.Verbose {
fmt.Printf("Starting %s\n", ln)
}
parts := strings.Fields(ln)
numbers := strings.Split(parts[1], ",")
groups := make([]int, len(numbers))
var err error
for i, v := range numbers {
groups[i], err = strconv.Atoi(v)
utils.Check(err, "Unable to convert %s to int64", v)
}
s := NewSequence([]rune(parts[0]), groups)
state := State{0, 0}
valid := s.CountValid(state)
if utils.Verbose {
fmt.Printf("\tValid: %d\n\n", valid)
}
sum += valid
}
return sum
}
// Memoize based on state
type State struct {
Start int
GroupsDone int
}
// Structure to hold problem and cache
type Sequence struct {
Row []rune
Groups []int
NGroups int // number of groups
RowLength int // length of row
Seen map[State]int64 // Previously seen results
}
func NewSequence(row []rune, groups []int) *Sequence {
s := Sequence{row, groups, len(groups), len(row), make(map[State]int64)}
return &s
}
func (s *Sequence) CountValid(state State) int64 {
if v, ok := s.Seen[state]; ok {
return v
}
// Check if there are no more groups to do
if state.GroupsDone >= s.NGroups {
valid := true
for i := state.Start; i < s.RowLength; i++ {
if s.Row[i] == '#' {
valid = false
break
}
}
if valid {
s.Seen[state] = 1
return 1
} else {
s.Seen[state] = 0
return 0
}
}
validSolutions := int64(0)
remainingSprings := s.RowLength - state.Start
// The number of springs that need to have a specific value are
// the remaining group counts plus a spacer between each group
accountedSprings := 0
for i := state.GroupsDone; i < s.NGroups; i++ {
accountedSprings += s.Groups[i]
accountedSprings++ // add spacer
}
accountedSprings-- // remove spacer for last one
// These are springs that are not spacers or broken springs in the rest of the
// problem
extraWorkingSprings := remainingSprings - accountedSprings
//
for buffer := 0; buffer < extraWorkingSprings+1; buffer++ {
valid := true
lastPosition := state.Start + buffer + s.Groups[state.GroupsDone]
for i := state.Start; i < lastPosition; i++ {
if i < state.Start+buffer && s.Row[i] == '#' {
valid = false
break
}
if i >= state.Start+buffer && s.Row[i] == '.' {
valid = false
break
}
}
if lastPosition < s.RowLength && s.Row[lastPosition] == '#' {
valid = false
}
if valid {
newState := State{lastPosition + 1, state.GroupsDone + 1}
validSolutions += s.CountValid(newState)
}
}
s.Seen[state] = validSolutions
return validSolutions
}