U V U V (a) |cfc|=2 prima e dopo (b) |cfc|=2 prima e |cfc|=1 dopo Esercizio Come si modifica il numero di cfc aggiungendo un arco? Trovare un (a) esempio in cui il numero di cfc non cambia, un (b) esempio in cui il numero di cfc diminuisce di 1 ed un (c) esempio in cui il numero di cfc da 10 diventa 1. Soluzione Esercizio 44 5 4 3 2 1 arco aggiunto 6 7 U V U V (b) |cfc|=10 prima e |cfc|=1 dopo 8 (a) |cfc|=2 prima e dopo (b) |cfc|=2 prima e |cfc|=1 dopo 9 10
Esercizio Dimostrare che il grafo delle cfc è aciclico (un DAG). Dimostrazione Supponiamo per assurdo che dato un grafo G il suo grafo delle cfc sia ciclico. Supponiamo (per semplicita’) che il ciclo coinvolga tre vertici, cioe’ sia: C1C2 C3C1. L’arco tra C1 e C2 nel grafo delle cfc implica ∃u∈C1 e ∃v∈C2 con uv∈E(G). Similmente, l’arco da C2 a C3 nel grafo delle cfc implica ∃x∈C2 e ∃y∈C3 con xy∈E(G). Infine, l’arco da C3 a C1 nel grafo delle cfc implica ∃r∈C3 e ∃s∈C1 con rs∈E(G). Ma allora avrei i cammini: uv⇝xy y⇝rs⇝u che contraddice il fatto che C1 e C3 sono cfc distinte.
Albero di connessione minimo Dato un grafo non orientato G = (V,E) connesso, un albero di connessione è costituito da un insieme aciclico T E di archi che connette tutti i vertici del grafo. Un grafo connesso G = (V,E) può avere molti alberi di connessione ma tutti hanno esattamente n - 1 archi. Albero di conn. minimo.
Due alberi di connessione dello stesso grafo: h g f e g a h a e h g f d c b i b h g f i e c d
Se associamo un peso w(uv) ad ogni arco uv allora gli alberi di connessione possono avere pesi diversi. a e h g f d c b i 4 10 14 2 7 6 1 8 11 9 peso = 37 a e h g f d c b i 4 10 14 2 7 6 1 8 11 9 peso = 42
Un albero di connessione minimo è un albero di connessione di peso minimo. Vi sono due algoritmi golosi per calcolare un albero di connessione minimo, dovuti rispettivamente a Kruskal e a Prim.
Entrambi gli algoritmi sono istanze di uno stesso algoritmo generale che costruisce l’insieme A degli archi di un albero di connessione minimo partendo dall’insieme vuoto e aggiungendo di volta in volta un arco sicuro per A ossia un arco uv tale che Auv sia sottoinsieme degli archi di qualche albero di connessione minimo.
AlbConnMinGenerico(G) G grafo pesato sugli archi while “A non forma un albero di connessione” do “cerca un arco sicuro uv” “aggiungi uv all’insieme A” return A Gli algoritmi di Kruskal e di Prim differiscono per il modo in cui viene cercato un arco sicuro (oltre che per la struttura dati usata per mantenere l’insieme A). Per capire questi algoritmi ci serve qualche altra definizione sui grafi.
Un taglio in un grafo non orientato G = (V,E) è una partizione dell’insieme di vertici V in due sottoinsiemi non vuoti (S,V \ S). Diciamo che un arco interseca il taglio (S,V \ S) se uno dei suoi estremi è in S e l’altro è in V \ S. Diciamo che il taglio (S,V \ S) rispetta un insieme di archi A se nessun arco in A interseca il taglio. Un arco leggero per un taglio è un arco di peso minimo tra tutti quelli che intersecano il taglio.
Vediamo queste definizioni su di un esempio. 7 8 b c d 4 9 2 4 11 14 a i e 7 6 10 8 h g f 1 2 S Arco leggero per il taglio V \ S Un insieme rispettato dal taglio
Caratterizzazione degli archi sicuri Caratterizzazione degli archi sicuri. Sia A un insieme di archi contenuto in qualche albero di connessione minimo, sia (S,V \ S) un taglio che rispetta A e sia uv un arco leggero per il taglio (S,V \ S). Allora l’arco uv è sicuro. Dimostrazione. Sia A un insieme di archi contenuto in un albero di connessione minimo T. Se T contiene anche l’arco uv siamo a posto.
Se T non contiene l’arco uv aggiungendo tale arco a T si forma un ciclo. Siccome u e v stanno da parti opposte rispetto al taglio deve esserci almeno un arco xy nel cammino da u a v in T che interseca il taglio. x xy p u y uv v
Sia T' l’albero di connessione ottenuto da T togliendo l’arco xy e aggiungendo l’arco uv. Siccome sia uv che xy intersecano il taglio ed uv è leggero, il peso di T' è minore o uguale del peso di T. Ma T è albero di connessione minimo e quindi anche T' lo è. Siccome il taglio rispetta l’insieme A l’arco tolto non stava in A e quindi T' contiene sia l’arco uv che gli archi in A. Pertanto uv è un arco sicuro.
Caso particolare. Sia A un insieme di archi contenuto in qualche albero di connessione minimo, sia C V una componente connessa del sottografo GA = (V,A) e sia uv un arco di peso minimo tra tutti quelli aventi un estremo in C e l’altro V \ C. Allora l’arco uv è sicuro. Dimostrazione. (C, V \ C) è un taglio che rispetta A e l’arco uv è un arco leggero rispetto al taglio.
Dimostrazione Ricordiamo che un grafo non orientato si dice connesso se esiste almeno un cammino tra ogni coppia di vertici. Le componenti connesse di un grafo sono le classi di equivalenza dei suoi vertici rispetto alla relazione di accessibilità. Notiamo che non ci puo’ essere nessun arco tra vertici di componenti connesse distinte (C, V \ C) è un taglio che rispetta A e l’arco uv è un arco leggero rispetto al taglio.
Mostrare che due alberi di connessione minimi Esercizio 46 Esercizio Supponiamo che il grafo G abbia più alberi di connessione minimi. Mostrare che due alberi di connessione minimi T e T' non solo hanno lo stesso peso totale ma anche i pesi dei singoli archi sono a due a due gli stessi. In altre parole se w1 w2 ... wn è la lista ordinata dei pesi degli archi di T e w'1 w'2 ... w'n è la lista ordinata dei pesi degli archi di T' allora wi = w'i per ogni i.
Algoritmo di Kruskal. Usa una struttura dati per insiemi disgiunti. AlbConnMinKruskal(G) G grafo pesato sugli archi A insieme vuoto for “ogni u V[G]” do MakeSet(u) “ordina gli archi in ordine di peso crescente” for “ogni arco uv E[G] in ordine di peso” do if FindSet(u) FindSet(v) then Union(u,v) “aggiungi uv all’insieme A” return A
8 7 b c d 4 9 2 4 11 14 a i e 6 7 8 10 h g f 2 1
Complessità. Il primo ciclo for richiede O(n) Complessità. Il primo ciclo for richiede O(n). L’ordinamento degli archi richiede O(m log m). L’ultimo ciclo for richiede O(m (m,n)). Pertanto la complessità è O(m log m) e siccome log m = O(log n2) = O(log n) essa è anche uguale a O(m log n). Corettezza. L’arco uv scelto è quello di peso minimo tra quelli che hanno estremi in componenti connesse distinte di GA = (V,A).
Algoritmo di Prim. Costruisce l’albero di connessione minimo partendo da un vertice prescelto come radice ed estendendolo finchè non connette tutti i vertici. Usa una coda con priorità Q in cui memorizza i vertici non ancora raggiunti dall’albero in costruzione.
I vertici sono aumentati con: un puntatore π al padre nell’albero in costruzione, un campo color che è bianco inizialmente e diventa nero quando il vertice viene aggiunto all’albero in costruzione un campo key che contiene il peso minimo di un arco che connette il vertice ad uno dei vertici già raggiunti dall’albero in costruzione.
AlbConnMinPrim(G) G grafo pesato sugli archi for “ogni u V[G]” do key[u] color[u] bianco “scegli un vertice r come radice” key[r] 0 “inserisci tutti i vertici in Q” Q coda con priorità while not Empty(Q) do u ExtractMin(Q) color [u] nero for “ogni v Adj[u]” do if color[v] = bianco and w(uv) < key[v] then π[v] u key[v] w(uv) Decrease-Key(Q,v,w(uv))
a e h g f d c b i 4 10 14 2 7 6 1 8 11 9
Complessità. Il primo ciclo for richiede tempo O(n). Usando un mucchio di Fibonacci per implementare la coda Q anche l’inserimento di tutti i vertici nella coda richiede tempo O(n). Il ciclo while viene eseguito n volte. Siccome ExtractMin richiede tempo O(log n), se escludiamo l’esecuzione dei cicli for interni esso richiede tempo O(n log n). complessità
I cicli for interni visitano tutte le liste delle adiacenze dei vertici. Siccome DecreaseKey richiede tempo O(1), essi richiedono tempo O(m). Pertanto la complessità è O(m + n log n).
Quindi uv è un arco sicuro. Correttezza. Quando il vertice u viene estratto dalla coda Q esso è connesso al vertice v = π[u] dall’arco uv di peso minimo tra tutti quelli che collegano un vertice dell’albero ad un vertice esterno all’albero. Quindi uv è un arco sicuro. correttezza