func problem68() int { const start = 6 // Each line cannot sum to less than 6 (1+2+3) const end = 27 // or greater than 27 (8+9+10) // Use a depth-first search. var frontier tools.Stack var solutions [][]int // Populate the initial frontier. for N := start; N <= end; N++ { for i := 1; i <= 10; i++ { r := Ring{ solution: []int{i}, used: make(map[int]bool), N: N, } r.used[i] = true frontier.Push(r) } } for len(frontier) > 0 { r := frontier.Pop().(Ring) if len(r.solution) == 15 { // Each solution can be described uniquely starting from the // group of three with the numerically lowest external node and // working clockwise. So we need to filter for solutions where // a < min(d, f, h, j) s := r.solution if s[0] < tools.Min(s[3], s[6], s[9], s[12]) { solutions = append(solutions, s) } } for _, child := range successors(r) { frontier.Push(child) } } // Process solutions according to the problem spec. var strlen = 16 var answers []int for _, sol := range solutions { var ss []string for _, x := range sol { ss = append(ss, strconv.Itoa(x)) } s := strings.Join(ss, "") if len(s) == strlen { val, _ := strconv.Atoi(s) answers = append(answers, val) } } return tools.Max(answers...) }
// Recursively find the maximum value of the root node plus the largest // of its children, and so on, all the way to the base. func maxRoute(tr Triangle) int { if val, ok := cache[k(tr)]; ok { return val } root := tr[0][0] if len(tr) == 1 { return root } a, b := children(tr) result := root + tools.Max(maxRoute(a), maxRoute(b)) cache[k(tr)] = result return result }
// We are going to use a concurrent depth-first search with a worker goroutine // pool of 4. Each goroutine will search for a solution from a different // starting prime. As soon as a solution is found, we return from the function. // Otherwise, we wait for all starting primes to be checked, and return an // error. func problem60() (int, error) { // It's not clear how many primes to search through. Experimentation // suggests a limit of 9000 produces the correct answer: 26033. Note // our algorithm does not guarantee the solution is the smallest // possible, but as a matter of fact, it is. We could verify our // answer by raising the limit to 26033, searching exhaustively, and // observing that no smaller solutions are found. limit := 9000 var primes []int for i := 0; i < limit; i++ { if tools.IsPrime(i) { primes = append(primes, i) } } c := newCache() ans := make(chan int) // Used to send the answer. done := make(chan bool) // Used to signal that all worker goroutines are done. pchan := make(chan int) // Used to send worker goroutines a starting prime to search. var wg sync.WaitGroup // Woker goroutine pool of 4. for i := 0; i < 4; i++ { wg.Add(1) go func() { for { p, ok := <-pchan if !ok { wg.Done() return } // Perform depth-first search starting at the given prime. var frontier tools.Stack frontier.Push(Node{p}) for len(frontier) != 0 { n := frontier.Pop().(Node) if len(n) == 5 { ans <- tools.Sum(n...) } for _, prime := range primes { child := append(append(*new(Node), n...), prime) if prime > tools.Max(n...) && c.allConcatToPrime(child) { frontier.Push(child) } } } } }() } go func() { for _, p := range primes { pchan <- p } close(pchan) wg.Wait() // Wait for all workers to complete their search done <- true // before sending completion signal. }() for { select { case x := <-ans: return x, nil case <-done: return -1, fmt.Errorf("problem60: no solution found with limit %v", limit) } } }