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] }