generated from devries/aoc_template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
solution.go
144 lines (121 loc) · 2.94 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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package day12p2
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)*5)
for i, v := range numbers {
n, err := strconv.Atoi(v)
utils.Check(err, "Unable to convert %s to int64", v)
for j := 0; j < 5; j++ {
groups[i+len(numbers)*j] = n
}
}
var bld strings.Builder
for i := 0; i < 5; i++ {
bld.WriteString(parts[0])
if i < 4 {
bld.WriteString("?")
}
}
if utils.Verbose {
fmt.Printf("\texpanded: %s\n", bld.String())
}
s := NewSequence([]rune(bld.String()), 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
}