package common import ( "bufio" "fmt" "strconv" "strings" ) type Valve struct { name string rate int tunnels map[*Valve]struct{} distances map[*Valve]int } func (valve *Valve) GetBestPathFromValveWithHelper(otherValves map[*Valve]int, minutesLeft int, helperValve *Valve, helperMinutesLeft int) int { max := 0 current := 0 if minutesLeft > 0 { current += (minutesLeft - 1) * valve.rate } if helperMinutesLeft > 0 { current += (helperMinutesLeft - 1) * helperValve.rate } if minutesLeft > 0 || helperMinutesLeft > 0 { for relevantValve := range(otherValves) { done := false if minutesLeft <= 0 { done = true } if minutesLeft - valve.distances[relevantValve] <= 0 { relevantValve = valve minutesLeft = 0 } for relevantValve2 := range(otherValves) { helperDone := false if helperMinutesLeft <= 0 { helperDone = true } if helperMinutesLeft - helperValve.distances[relevantValve2] <= 0 { relevantValve2 = helperValve helperMinutesLeft = 0 } if relevantValve != relevantValve2 { currentRelevantValves := make(map[*Valve]int) for relevantValve3, relevantValveRate3 := range(otherValves) { if relevantValve != relevantValve3 && relevantValve2 != relevantValve3 { currentRelevantValves[relevantValve3] = relevantValveRate3 } } currentMax := relevantValve.GetBestPathFromValveWithHelper(currentRelevantValves, minutesLeft - 1 - valve.distances[relevantValve], relevantValve2, helperMinutesLeft - 1 - helperValve.distances[relevantValve2]) if currentMax > max { max = currentMax } } else if len(otherValves) == 1 { currentMax := relevantValve.GetBestPathFromValveWithHelper(make(map[*Valve]int), minutesLeft - 1 - valve.distances[relevantValve], helperValve, 0) if currentMax > max { max = currentMax } currentMax = valve.GetBestPathFromValveWithHelper(make(map[*Valve]int), 0, relevantValve2, helperMinutesLeft - 1 - helperValve.distances[relevantValve2]) if currentMax > max { max = currentMax } } if helperDone { break } } if done { break } } } return max + current } func (valve *Valve) GetBestPathFromValve(otherValves map[*Valve]int, minutesLeft int) int { if minutesLeft <= 0 { return 0 } current := (minutesLeft - 1) * valve.rate max := 0 for relevantValve := range(otherValves) { currentRelevantValves := make(map[*Valve]int) for relevantValve2, relevantValveRate2 := range(otherValves) { if relevantValve != relevantValve2 { currentRelevantValves[relevantValve2] = relevantValveRate2 } } currentMax := relevantValve.GetBestPathFromValve(currentRelevantValves, minutesLeft - 1 - valve.distances[relevantValve]) if currentMax > max { max = currentMax } } return max + current } type Volcano struct { valves []*Valve current *Valve rate int pressure int minutesLeft int } func (volcano *Volcano) GetBestPath(minutesLeft int, helper bool) int { relevantValves := make(map[*Valve]int) for valve := range volcano.valves { if volcano.valves[valve].rate > 0 { relevantValves[volcano.valves[valve]] = volcano.valves[valve].rate } } if helper { return volcano.current.GetBestPathFromValveWithHelper(relevantValves, minutesLeft + 1, volcano.current, minutesLeft + 1) } else { return volcano.current.GetBestPathFromValve(relevantValves, minutesLeft + 1) } } func (volcano *Volcano) ComputeDistances() bool { changesFound := false for valveSource := range volcano.valves { for valveTarget1 := range volcano.valves[valveSource].distances { for valveTarget2 := range volcano.valves[valveSource].distances { d, ok := valveTarget1.distances[valveTarget2] newDistance := volcano.valves[valveSource].distances[valveTarget1] + volcano.valves[valveSource].distances[valveTarget2] if !ok || newDistance < d { valveTarget1.distances[valveTarget2] = newDistance valveTarget2.distances[valveTarget1] = newDistance changesFound = true } } } } return changesFound } func (volcano *Volcano) Print() { for i := range(volcano.valves) { fmt.Print(volcano.valves[i].name) fmt.Print(": ") fmt.Println(volcano.valves[i].rate) for valve, d := range(volcano.valves[i].distances) { fmt.Print(" ") fmt.Print(valve.name) fmt.Print(": ") fmt.Println(d) } } } func Parse(scanner bufio.Scanner) Volcano { volcano := Volcano{} volcano.valves = []*Valve{} destinations := make(map[string][]string) for scanner.Scan() { line := scanner.Text() splittedLine := strings.Split(line, " ") name := splittedLine[1] rate, _ := strconv.Atoi(splittedLine[4][5:len(splittedLine[4])-1]) destinations[name] = []string{} for i := 9; i < len(splittedLine); i++ { destinations[name] = append(destinations[name], splittedLine[i][0:2]) } volcano.valves = append(volcano.valves, &Valve{name, rate, map[*Valve]struct{}{}, make(map[*Valve]int)}) } for valveName, currDestinations := range(destinations) { var valveSource *Valve for i := range volcano.valves { if volcano.valves[i].name == valveName { valveSource = volcano.valves[i] valveSource.distances[valveSource] = 0 break } } for _, destination := range currDestinations { var valveDestination *Valve for i := range volcano.valves { if volcano.valves[i].name == destination { valveDestination = volcano.valves[i] } } valveSource.tunnels[valveDestination] = struct{}{} valveSource.distances[valveDestination] = 1 } } for i := range(volcano.valves) { if volcano.valves[i].name == "AA" { volcano.current = volcano.valves[i] } } volcano.minutesLeft = 30 for volcano.ComputeDistances() {} return volcano }