Informatyczny kącik olimpijski
Trening
Tym razem przyjrzymy się zadaniu Training z Międzynarodowej Olimpiady Informatycznej 2007.
Zadanie. Mamy dany spójny graf nieskierowany o
wierzchołkach
z wyróżnionym drzewem rozpinającym (tzn.
wyróżnione jest
krawędzi, które tworzą drzewo). Krawędzie
spoza tego drzewa mają przypisane koszty. Należy przeciąć krawędzie
o najmniejszej sumie kosztów, tak aby w grafie nie pozostał żaden cykl
prosty (tzn. taki, w którym nie powtarzają się wierzchołki) długości
parzystej. Krawędzi drzewowych nie wolno przecinać. Dodatkowo wiadomo,
że stopień żadnego z wierzchołków grafu nie przekracza (niedużego)
Krawędzie spoza drzewa będę nazywał poprzecznymi. Każda poprzeczna
krawędź tworzy z naszym drzewem dokładnie jeden cykl (nazwijmy go cyklem
poprzecznym). Jeśli cykl ten jest długości parzystej, musimy tę krawędź
przeciąć, robimy więc to czym prędzej i nie zajmujemy się więcej taką
krawędzią. Zauważmy, że w poprawnym rozwiązaniu możemy pozostawić
cykle poprzeczne długości nieparzystej, ale tylko pod warunkiem, że żadne
dwa z nich nie mają wspólnej krawędzi. Gdyby bowiem miały, to przez ich
złączenie i usunięcie części wspólnej uzyskalibyśmy cykl prosty długości
przy czym
i
są długościami tych cykli, a
– długością części
wspólnej. Cykli poprzecznych bez wspólnych krawędzi nie da się w żaden
sposób połączyć w cykl prosty. Zadanie sprowadza się więc do wybrania
poprzecznych krawędzi, które możemy pozostawić, tak aby odpowiadające im
cykle poprzeczne nie miały wspólnych krawędzi.
Do rozwiązania tak uproszczonego problemu użyjemy programowania
dynamicznego. Ukorzeńmy nasze drzewo rozpinające. Dla każdego
wierzchołka
i dla każdego podzbioru
synów
w naszym drzewie obliczymy
– największą możliwą sumę
kosztów poprzecznych krawędzi, które możemy pozostawić, a których
cykle są rozłączne krawędziowo i zawierają się całkowicie w podgrafie
złożonym z krawędzi łączących
z wierzchołkami z
poddrzew
wierzchołków z
oraz poprzecznych krawędzi.
Aby obliczyć
musimy zastanowić się nad cyklami
poprzecznymi, które przechodzą przez
ale nie przechodzą przez ojca
Jeśli
jest pusty, to
W przeciwnym
przypadku ustalmy pierwszego syna
Załóżmy, że istnieje cykl
poprzeczny, który zawiera
i
ale nie zawiera ojca
Niech
będzie krawędzią poprzeczną tegoż cyklu, a
– zbiorem
jego wierzchołków. Wtedy przy obliczaniu
należy uwzględnić
wyrażenie:
![]() | (1) |
przy czym
oznacza zbiór wszystkich synów wierzchołka
Możemy też nie zdecydować się wziąć żadnego takiego cyklu, czemu
odpowiada wyrażenie:
![]() | (2) |
Ostatecznie jako
weźmiemy maksimum z wartości (1) po
odpowiednich parach
oraz (2).
Odpowiedzią w naszym zadaniu będzie

przy czym
jest sumą kosztów wszystkich krawędzi poprzecznych, a
– korzeniem drzewa.
Zastanówmy się nad złożonością czasową tego rozwiązania. Graf ma
krawędzi. Dla każdej krawędzi poprzecznej musimy znaleźć
jej cykl w czasie
co daje łączny koszt
Każda
krawędź poprzeczna
jest rozważana podczas programowania
dynamicznego tylko dla wierzchołka
będącego najwyższym
wierzchołkiem w drzewie, do którego sięga cykl poprzeczny zawierający
Stąd sumę po wierzchołkach tego cyklu, występującą w wyrażeniu (1),
wystarczy obliczyć raz. Dla każdej pary
wybieramy albo brak
krawędzi poprzecznych, albo też jedną z krawędzi, których cykle przechodzą
przez
oraz pierwszego syna
w
ale nie przez ojca
Niech takich właśnie krawędzi poprzecznych będzie
a wszystkich krawędzi, których cykle przechodzą przez
ale
nie przechodzą przez jego ojca –
Wtedy
![]() |
Programowanie dynamiczne zajmuje więc czas
plus
obliczanie sum po wstawianych cyklach, którego koszt czasowy jest taki
sam jak koszt szukania tych cykli. Stąd łączna złożoność czasowa to
przy zużyciu pamięci rzędu