refactor+feat: use bitmask approach and cyclic/acyclic dfs for resolution
This commit is contained in:
@@ -2,6 +2,7 @@ package dayeleven
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"advent-of-code/internal/registry"
|
"advent-of-code/internal/registry"
|
||||||
@@ -16,7 +17,7 @@ func ParseInput(filepath string) []string {
|
|||||||
return strings.Split(string(content), "\n")
|
return strings.Split(string(content), "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func PartOne(data []string) int {
|
func buildGraph(data []string) map[string][]string {
|
||||||
graph := make(map[string][]string)
|
graph := make(map[string][]string)
|
||||||
|
|
||||||
for _, line := range data {
|
for _, line := range data {
|
||||||
@@ -24,22 +25,129 @@ func PartOne(data []string) int {
|
|||||||
graph[device] = strings.Fields(outputsStr)
|
graph[device] = strings.Fields(outputsStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
var countPaths func(string) int
|
return graph
|
||||||
countPaths = func(node string) int {
|
}
|
||||||
if node == "out" {
|
|
||||||
|
func hasCycle(graph map[string][]string) bool {
|
||||||
|
const (
|
||||||
|
unvisited = iota
|
||||||
|
visiting
|
||||||
|
visited
|
||||||
|
)
|
||||||
|
|
||||||
|
state := make(map[string]uint8)
|
||||||
|
|
||||||
|
var dfs func(node string) bool
|
||||||
|
dfs = func(node string) bool {
|
||||||
|
switch state[node] {
|
||||||
|
case visiting:
|
||||||
|
return true
|
||||||
|
case visited:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
state[node] = visiting
|
||||||
|
if slices.ContainsFunc(graph[node], dfs) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
state[node] = visited
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for node := range graph {
|
||||||
|
if state[node] == unvisited {
|
||||||
|
if dfs(node) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func countPaths(graph map[string][]string, start, end string, requiredNodes map[string]bool) int {
|
||||||
|
requiredIndex := make(map[string]uint)
|
||||||
|
var targetMask uint
|
||||||
|
for node := range requiredNodes {
|
||||||
|
requiredIndex[node] = uint(len(requiredIndex))
|
||||||
|
targetMask |= 1 << requiredIndex[node]
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasCycle(graph) {
|
||||||
|
var dfs func(node string, visited map[string]bool, seenMask uint) int
|
||||||
|
dfs = func(node string, visited map[string]bool, seenMask uint) int {
|
||||||
|
if node == end {
|
||||||
|
if seenMask != targetMask {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if idx, ok := requiredIndex[node]; ok {
|
||||||
|
seenMask |= 1 << idx
|
||||||
|
}
|
||||||
|
|
||||||
|
visited[node] = true
|
||||||
|
defer delete(visited, node)
|
||||||
|
|
||||||
count := 0
|
count := 0
|
||||||
for _, neighbor := range graph[node] {
|
for _, neighbor := range graph[node] {
|
||||||
count += countPaths(neighbor)
|
if !visited[neighbor] {
|
||||||
|
count += dfs(neighbor, visited, seenMask)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
return countPaths("you")
|
return dfs(start, make(map[string]bool), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
type key struct {
|
||||||
|
node string
|
||||||
|
mask uint
|
||||||
|
}
|
||||||
|
memo := make(map[key]int)
|
||||||
|
|
||||||
|
var dfs func(node string, seenMask uint) int
|
||||||
|
dfs = func(node string, seenMask uint) int {
|
||||||
|
if node == end {
|
||||||
|
if seenMask != targetMask {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if idx, ok := requiredIndex[node]; ok {
|
||||||
|
seenMask |= 1 << idx
|
||||||
|
}
|
||||||
|
|
||||||
|
state := key{node: node, mask: seenMask}
|
||||||
|
if cached, ok := memo[state]; ok {
|
||||||
|
return cached
|
||||||
|
}
|
||||||
|
|
||||||
|
count := 0
|
||||||
|
for _, neighbor := range graph[node] {
|
||||||
|
count += dfs(neighbor, seenMask)
|
||||||
|
}
|
||||||
|
|
||||||
|
memo[state] = count
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
return dfs(start, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
graph := buildGraph(data)
|
||||||
|
return countPaths(graph, "you", "out", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func PartTwo(data []string) int {
|
func PartTwo(data []string) int {
|
||||||
return 0
|
graph := buildGraph(data)
|
||||||
|
requiredNodes := map[string]bool{
|
||||||
|
"dac": true,
|
||||||
|
"fft": true,
|
||||||
|
}
|
||||||
|
return countPaths(graph, "svr", "out", requiredNodes)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user