Zadanie

Dzisiejsze zadanie jest klasycznym zadaniem z teorii grafów. W naszym zadaniu komputery to wierzchołki, oznaczone są one jako stringi składające się z dwóch znaków, w danych wejściowych dostajemy listę krawędzi grafu w postaci:

kh-tc  # komputer kh jest połączony z komputerem tc
qp-kh
de-cg
ta-ka
...

Mamy znaleźć takie zbiory trzech komputerów że każdy jest połączony z każdym. Jest to problem znalezienia kliki w grafie. Problem ten jest NP-trudny, jednak dla klik o rozmiarach 3 nawet rozwiązanie typu brute force nie powinno sprawić większego problemu.

Dodatkowo mamy zwrócić tylko te kliki które posiadają choć jeden wierzchołek który zaczyna się na literę t.

Rozwiązanie

import sys
from collections import defaultdict
from itertools import combinations

graph = defaultdict(list)

# tworzymy listę sąsiedztwa reperezentującą graf
for line in sys.stdin.readlines():
    a, b = line.strip().split("-")
    graph[a].append(b)
    graph[b].append(a)

# zliczymy ile razy dana trójka wierzchołków się powtarza
counts = defaultdict(int)
for k, v in graph.items():
    for x, y in combinations(v, 2): # wszystkie pary sąsiadów
        counts[frozenset([k, x, y])] += 1

# filtrujemy tylko te trójki które trafiły się 3 razy
# i jeśli choć jeden z nich się zaczyna na literę t
part1 = 0
for s, count in counts.items():
    if count == 3 and any(l.startswith("t") for l in s):
        part1 += 1

print(part1)

Po uruchomieniu dostajemy poprawną odpowiedź

Część druga

Do zdobycia drugiej gwiazdki musimy się bardziej postarać, teraz musimy znaleźć największą możliwą klikę! W tym przypadku brute force nie zadziała gdyż ilośc kombinacji robi się zbyt duża, jak wspominałem problem jest NP-trudny, ale istnieją różne algorytmy które znajdują rozwiązanie w krótkim czasie o ile input nie jest za duży a w tym przypadku nie jest.

Jestem trochę zmęczony po 22 dniach pisania więc uznałem że pójdę na łatwiznę i użyję gotowej implementacji z paczki networkx:

import sys

import networkx as nx

G = nx.Graph()
for l in sys.stdin.readlines():
    a, b = l.strip().split("-")
    G.add_edge(a, b)

print(",".join(sorted(max(nx.find_cliques(G), key=lambda c: len(c)))))

Podsumowanie

Dzisiaj kluczowym było rozpoznanie problemu jako problemu znalezienia klik w grafie i użycie odpowiedniej paczki lub zaimplementowanie algorytmu który to robi. Myślę że warto się lepiej zaznajomić z paczką networkx bo wczytując się w nią powierzchownie widziałem że jest ona dosyć rozbudowana i posiada wiele ciekawych funkcji.