import collections import itertools import re text = open(0).read() graph = collections.defaultdict(dict) rates = {} for ln in text.splitlines(): source, rate, *leads_to = re.findall(r'[A-Z]{2}|\d+', ln) for out in leads_to: graph[out][source] = 1 graph[source][out] = 1 rates[source] = int(rate) costs = {} for k in graph: edge = set(graph[k]) seen = set() cost = 1 while True: for e in edge: if (k, e) in costs: costs[k, e] = min(costs[k, e], cost) else: costs[k, e] = cost cost += 1 edge = {n for e in edge for n in graph[e]} - seen if seen == set(graph): break seen |= edge def for_one(combo, lim): tt = 0 bob = [0] for a, b in zip(('AA',) + combo, combo): tt = costs[a, b] for _ in range(tt): bob.append(bob[-1]) bob.append(bob[-1] + rates[b]) pending = max(0, lim - len(bob)) bob += [bob[-1]] * pending return sum(bob[:lim]) def generate_combos(pending, left, carry=('AA',)): if left < 0 or not pending: yield carry[1:] else: for k in pending: yield from generate_combos(pending - {k}, left - costs[carry[-1], k], carry + (k,)) combos = generate_combos({k for k, v in rates.items() if v}, lim=30) best = max(combos, key=for_one) print(best) print(for_one(best)) # 2615