2025-12-10 06:10:56 +00:00

111 lines
2.0 KiB
Go

package main
import (
"bufio"
"container/heap"
"fmt"
"log"
"os"
"strings"
)
func die(err error) {
log.Fatal(err)
}
func check(err error) {
if err != nil {
die(err)
}
}
func main() {
solve("sample")
solve("input")
}
func solve(filename string) {
input, err := os.Open(filename + ".x")
check(err)
scanner := bufio.NewScanner(input)
scanner.Split(bufio.ScanLines)
total := 0
for scanner.Scan() {
line := scanner.Text()
parts := strings.Fields(line)
var nums []uint32
for _, p := range parts {
var x uint32
fmt.Sscanf(p, "0x%x", &x)
nums = append(nums, x)
}
//fmt.Printf("%b %v\n", nums, parts)
n := best(nums[0], nums[1:])
fmt.Printf("%b %v\n", nums, n)
total += n
//fmt.Println(n)
}
check(scanner.Err())
fmt.Println(total)
}
// An CostHeap is a min-heap of ints.
type CostHeap struct {
heap []uint32
cost map[uint32]int
}
func (h *CostHeap) Len() int { return len(h.heap) }
func (h *CostHeap) Less(i, j int) bool { return h.cost[h.heap[i]] < h.cost[h.heap[j]] }
func (h *CostHeap) Swap(i, j int) { h.heap[i], h.heap[j] = h.heap[j], h.heap[i] }
func (h *CostHeap) Push(x any) {
// Push and Pop use pointer receivers because they modify the slice's length,
// not just its contents.
h.heap = append(h.heap, x.(uint32))
}
func (h *CostHeap) Pop() any {
old := h.heap
n := len(old)
x := old[n-1]
h.heap = old[0 : n-1]
return x
}
func best(target uint32, pool []uint32) int {
//var states = []uint32{}
var cost = make(map[uint32]int)
h := &CostHeap{nil, cost}
heap.Push(h, uint32(0))
for h.Len() > 0 {
s := heap.Pop(h).(uint32)
if s == target {
break
}
// enumerate all the states that can be reached by toggling button p
for _, p := range pool {
t := s ^ p
if cost_t, ok := cost[t]; ok {
if cost[s]+1 < cost_t {
cost[t] = cost[s] + 1
for i, x := range h.heap {
if x == t {
heap.Fix(h, i)
break
}
}
}
} else {
cost[t] = cost[s] + 1
//states = append(states, t)
heap.Push(h, t)
}
}
}
return cost[target]
}