Capitolo 6 Problemi di Cammino Minimo Dato un grafo orientato D = (V, A), diciamo che sono specificati costi sugli archi di D se è assegnato un valore ce per ogni e ∈ A. Dato un sottoinsieme di archi S ⊆ A, chiamiamo costo di S il numero c(S) = ∑e∈S ce . Il problema del cammino minimo è il seguente: dati un grafo orientato D = (V, A) con costi (ce )e∈A sugli archi ed un nodo s ∈ V (detto sorgente), trovare, per ogni v ∈ V , un cammino orientato di costo minimo da s a v (o stabilire che non c’è alcun cammino orientato da s a v).1 Nel resto del capitolo lavoreremo sempre con cammini e cicli orientati. Pertanto, spesso diremo semplicemente “cammino” o “ciclo” invece di “cammino orientato” e “ciclo orientato”. Un cammino minimo da s a v è un cammino di costo minimo da s a v. Se non si impone alcuna condizione sui costi, non è noto alcun algoritmo polinomiale per risolvere il problema del cammino minimo. Si può dimostrare che se esistesse un tale algoritmo, allora potremmo risolvere in tempo polinomiale anche il problema del ciclo hamiltoniano su un qualunque grafo (Esempio 5.8), cosa che, come detto nel capitolo precedente, è ritenuta molto improbabile a causa di profonde motivazioni teoriche. Per vedere come un eventuale algoritmo polinomiale per il problema del cammino minimo potrebbe essere usato per risolvere il problema del ciclo hamiltoniano, procediamo come segue. Supponiamo che esista un tale algoritmo A per il problema del cammino minimo e sia G = (V, E) una qualunque istanza del problema del ciclo hamiltoniano, che vogliamo risolvere. Scegliamo arbitrariamente un nodo s ∈ V . Si noti che se esiste un ciclo hamiltoniano in G, allora tale ciclo contiene almeno uno spigolo e ∈ δ(s) (in realtà ne contiene esattamente due). Allora, per decidere se G ha un ciclo hamiltoniano, è sufficiente verificare, per ogni e ∈ δ(s), se G ha un ciclo hamiltoniano contenente lo spigolo e. Fissato uno spigolo e ∈ δ(s), diciamo e = st, facciamo quanto segue: rimuoviamo e da G, ottenendo un nuovo grafo G′ ; costruiamo un grafo orientato D a partire da G′ , sostituendo ogni spigolo uv ∈ E con una coppia di archi opposti (u, v), (v, u); diamo costo −1 a tutti gli archi di D. Poiché un cammino non può visitare due volte lo stesso nodo, ogni cammino in D ha al massimo n − 1 archi (dove n è il numero di nodi del grafo), dunque il costo di un cammino minimo da s a t è sempre almeno −(n − 1); inoltre, tale costo è esattamente −(n − 1) se e solo se esiste un cammino da s a t che 1 In alcune formulazioni, nel problema del cammino minimo sono fissati due nodi s e t (dove t è detto terminazione o pozzo) e si richiede di trovare il cammino orientato di costo minimo da s a t. Tuttavia, quasi tutti gli algoritmi per questo problema calcolano contemporaneamente un cammino di costo minimo da s a qualunque terminazione v ∈ V , senza che questo richieda alcuno sforzo aggiuntivo. Per questo motivo non fissiamo la terminazione. 63 64 CAPITOLO 6. PROBLEMI DI CAMMINO MINIMO passi per tutti i nodi di D. In tal caso, ignorando l’orientamento degli archi e ripristinando lo spigolo e otteniamo un ciclo hamiltoniano in G; in caso contrario, invece, G non ha alcun ciclo hamiltoniano contenente lo spigolo e. Eseguendo dunque queste operazioni per ogni e ∈ δ(s), possiamo scoprire se G contiene un ciclo hamiltoniano. Questa procedura richiede l’applicazione dell’algoritmo A esattamente ∣δ(s)∣ volte (e quindi meno di n volte); dunque, se A è polinomiale, ne risulta una procedura polinomiale. Il problema del cammino minimo sembra dunque di difficile soluzione quando i costi sono arbitrari. Questo problema può tuttavia essere risolto in tempo polinomiale se il grafo non contiene alcun ciclo orientato di costo negativo, cioè se G non ha alcun ciclo orientato C tale che c(C) = ∑e∈A(C) < 0. Si noti che questa condizione è sempre soddisfatta, ad esempio, se i costi sono tutti non-negativi (caso di evidente interesse pratico). 6.1 Cammini di lunghezza minima Ci concentriamo prima sul caso particolare in cui ce = 1 per ogni e ∈ A; in questa situazione, il costo di un cammino è pari al numero di archi che lo compongono, cioè alla sua lunghezza. Il problema è dunque il seguente: dati un grafo orientato D = (V, A) ed un nodo s ∈ V , trovare, per ogni v ∈ V , un cammino di lunghezza minima da s a v (o stabilire che non c’è alcun cammino da s a v). Sia n = ∣V ∣. Si noti che qualunque cammino in D ha lunghezza inferiore o uguale a n − 1. Per ogni nodo v ∈ V , definiamo distanza di v da s la lunghezza di un cammino da s a v che ha il numero minimo di archi. Per k = 0, . . . , n − 1, indichiamo con Vk l’insieme dei nodi a distanza k da s. In altre parole un nodo v appartiene a Vk se e solo se il cammino minimo da s a v ha lunghezza k.2 L’algoritmo proposto per la risoluzione del problema in esame si basa sulle seguenti osservazioni. Lemma 6.1 V0 = {s}, mentre, per k = 1, . . . , n − 1, Vk = {v ∈ V ∖ (V0 ∪ V1 ∪ . . . ∪ Vk−1 ) ∶ (u, v) ∈ A per qualche u ∈ Vk−1 }. (6.1) Dimostrazione. È chiaro che V0 = {s}. Fissiamo ora k ∈ {1, . . . , n − 1} e definiamo Sk come l’insieme a secondo membro dell’uguaglianza (6.1). Vogliamo mostrare che Vk = Sk . Se v ∈ Vk allora necessariamente v ∉ V0 ∪ ⋅ ⋅ ⋅ ∪ Vk−1 . Inoltre, esiste un cammino minimo P da s a v la cui lunghezza è k. Sia u il nodo che precede v in P . Dimostriamo che il sottocammino Psu è un cammino di lunghezza minima da s a u. Supponiamo per assurdo che esista un cammino Q da s a u di lunghezza minore di quella di Psu . Se Q non contenesse v, allora la sequenza Q, (u, v), v formerebbe un cammino da s a v di lunghezza minore di quella di P , una contraddizione. Se, invece, Q contenesse v, allora Qsv sarebbe un cammino da s a v di lunghezza minore di quella di P , di nuovo una contraddizione. Dunque Psu è un cammino di lunghezza minima da s a u, il che implica che u ∈ Vk−1 . Questo mostra che Vk ⊆ Sk . Per verificare l’inclusione inversa, osserviamo che se v ∈ Sk allora la distanza di v da s è almeno k. D’altro canto, se v ∈ Sk allora esiste u ∈ Vk−1 tale che (u, v) ∈ A. Poiché u ∈ Vk−1 , esiste un cammino P da s a u di lunghezza k − 1. Si noti che v non può essere un nodo di P , altrimenti la sua distanza da s sarebbe inferiore a k. Allora la sequenza P, (u, v), v è un 2 Sarebbe più corretto dire “i cammini minimi da s a v hanno lunghezza k”, visto che il cammino minimo non è necessariamente unico (ma tutti i cammini minimi da s a v hanno ovviamente la stessa lunghezza). 6.1. CAMMINI DI LUNGHEZZA MINIMA 65 cammino da s a v di lunghezza k, quindi v ha distanza al massimo k da s. Questo implica che la distanza di v da s è esattamente k, cioè v ∈ Vk . Dunque Sk ⊆ Vk . ◻ Lemma 6.2 Sia (u, v) ∈ A, dove u ∈ Vk−1 e v ∈ Vk . Se Q è un cammino di lunghezza minima da s a u, allora la sequenza Q, (u, v), v costituisce un cammino di lunghezza minima da s a v. Dimostrazione. La dimostrazione è immediata, una volta osservato che v ∉ V (Q) (altrimenti la sua distanza da s sarebbe inferiore a k, come mostrato dal cammino Qsv ). ◻ L’algoritmo che proponiamo per determinare i cammini minimi dal nodo sorgente s ad ogni nodo v ∈ V costruirà iterativamente l’insieme Vk a partire dagli insiemi V0 , . . . , Vk−1 : questo può essere fatto grazie al Lemma 6.1. Allo stesso tempo, registreremo i predecessori di ogni nodo, cioè per ogni nodo v ∈ V memorizzeremo un nodo u tale che u, v soddisfino le ipotesi del Lemma 6.2 (in molti casi la scelta di u non è univoca, ma questo non crea alcuna difficoltà). Con queste informazioni avremo la soluzione del problema: per ogni nodo v, detto k l’indice tale che v ∈ Vk , sapremo che la lunghezza di un cammino minimo da s a v è k; inoltre, detto u il predecessore di v memorizzato dall’algoritmo, sapremo che un cammino minimo da s a v può essere costruito aggiungendo l’arco (u, v) ad un qualunque cammino minimo da s a u (Lemma 6.2); un tale cammino minimo può essere determinato a sua volta verificando qual è il predecessore del nodo u registrato dall’algoritmo e procedendo a ritroso, fino a risalire alla sorgente s. Formalmente, l’algoritmo manterrà, per ogni nodo v ∈ V , la distanza d(v) di v da s (dove d(v) = +∞ se tale distanza non è ancora stata determinata) ed un puntatore p(v) ad un nodo u tale che (u, v) ∈ A e d(u) = d(v) − 1. Manterremo anche aggiornato l’insieme R dei nodi già raggiunti (cioè quelli per cui è stata determinata la distanza da s). Diamo qui sotto la descrizione formale dell’algoritmo; si veda la Figura 6.1 per un esempio di applicazione dell’algoritmo. Algoritmo per cammini di lunghezza minima Input: Un grafo orientato D = (V, A) ed un nodo s ∈ V . Output: Per ogni v ∈ V , la lunghezza d(v) di un cammino di lunghezza minima da s a v ed un nodo p(v) che precede v in un tale cammino (se d(v) < +∞; altrimenti p(v) non è definito). 1. Si ponga d(s) ∶= 0, d(v) ∶= +∞ ∀v ∈ V ∖ {s}, V0 ∶= {s} e R ∶= {s}; 2. Per ogni k = 1, . . . , n − 1: 2.1. Si ponga Vk ∶= ∅; 2.2. Per ogni arco (u, v) tale che u ∈ Vk−1 e v ∉ R: si ponga d(v) ∶= k, p(v) ∶= u, Vk ∶= Vk ∪ {v} e R ∶= R ∪ {v}. Teorema 6.3 L’algoritmo per cammini di lunghezza minima funziona correttamente ed esegue O(∣V ∣ + ∣A∣) operazioni. 66 CAPITOLO 6. PROBLEMI DI CAMMINO MINIMO Dimostrazione. Sia k ∈ {1, . . . , n − 1}. Alla fine della k-esima iterazione dell’istruzione 2, gli insiemi V0 , . . . , Vk sono stati costruiti correttamente ed inoltre R = V0 ∪ ⋅ ⋅ ⋅ ∪ Vk : questo segue dalle istruzioni dell’algoritmo e dal Lemma 6.1. Si noti inoltre che d(v) < +∞ se e solo se v ∈ R (e in tal caso p(v) è definito e non muterà più). Pertanto, dopo l’ultima iterazione, a ciascun nodo v ∈ R è stato assegnato il corretto valore d(v) ed il corretto predecessore p(v). I nodi v ∉ R, invece, hanno d(v) = +∞: questi sono i nodi per i quali non esiste alcun cammino da s a v (se ci fosse un tale cammino, l’avremmo trovato prima della fine dell’algoritmo, dato che nessun cammino in D ha lunghezza maggiore di n − 1). Per quanto riguarda la complessità dell’algoritmo, il passo 1 e tutte le iterazioni del passo 2.1 richiedono complessivamente tempo O(∣V ∣). Inoltre, ogni arco (u, v) viene analizzato al massimo una volta durante le varie iterazioni dell’istruzione 2.2 (con un numero costante di operazioni per ogni arco). Ne segue che l’algoritmo esegue O(∣V ∣ + ∣A∣) operazioni. ◻ Osserviamo che può accadere di trovare R = V ben prima di raggiungere l’ultima iterazione dell’algoritmo; in tal caso si può interrompere l’esecuzione, in quanto tutti i cammini minimi sono stati determinati. In modo analogo, se dopo la k-esima iterazione si dovesse avere Vk = ∅, allora potremmo interrompere l’esecuzione, in quanto proseguendo troveremmo Vk+1 = ⋅ ⋅ ⋅ = Vn−1 = ∅. Come visto, al termine dell’algoritmo l’insieme R conterrà esattamente i nodi raggiungibili da s, cioè i nodi v ∈ V per i quali esiste un cammino da s a v. Per tali nodi, inoltre, p(v) è definito. Sia T = (R, A′ ) il sottografo di D con insieme dei nodi R ed insieme degli archi A′ = {(p(v), v) ∶ v ∈ R ∖ {s}}. È semplice dimostrare quanto segue. Osservazione 6.4 T è un’arborescenza con radice s. Per ogni v ∈ R, l’unico cammino da s a v in T è un cammino di lunghezza minima d(v). Dimostrazione. Per ogni v ∈ R, il grafo T contiene un cammino orientato da s a v (questo segue dalla correttezza dell’algoritmo). Per costruzione, ∣A′ ∣ = ∣R∣ − 1. Dunque T è un’arborescenza con radice s. La seconda affermazione segue ancora dalla correttezza dell’algoritmo. ◻ Si dice che T è un albero di cammini di lunghezza minima di D (si veda di nuovo la Figura 6.1, da cui si evince anche che tale albero non è unico). 6.2 L’algoritmo di Bellman–Ford L’algoritmo di Bellman–Ford risolve il seguente problema: dato un grafo orientato D = (V, A) con costi (ce )e∈A sugli archi, dove D non contiene alcun ciclo di costo negativo, e dato un nodo sorgente s ∈ V , trovare, per ogni v ∈ V , un cammino di costo minimo da s a v (o stabilire che non c’è alcun cammino da s a v). Da ora in avanti, dato un arco (u, v) ∈ A, indicheremo il suo costo con cuv invece di usare la notazione più pesante c(u,v) . Sia n = ∣V ∣. Dato un cammino orientato P da s ad un nodo v ∈ V e dato k ∈ {0, . . . , n − 1}, diciamo che P è un cammino k-ristretto se ha al massimo k archi. Per ogni v ∈ V , indichiamo con ℓk (v) il costo di un cammino k-ristretto da s a v di costo minimo, dove ℓk (v) = +∞ se non esiste alcun cammino k-ristretto da s a v. Proposizione 6.5 Sia D = (V, A) un grafo orientato, siano s, v ∈ V e siano (ce )e∈A costi tali che D non contenga alcun ciclo di costo negativo. Sia P un cammino k-ristretto di costo 67 6.2. L’ALGORITMO DI BELLMAN–FORD 1 ∞ s s s s∞ s 0 s 0 s∞ s s∞ s s ∞ ∞ s ∞ 1 s s ∞ ∞ 1 1 s s s∞ s s s2 s 0 s 0 s2 s s2 s s ∞ 1 s2 s s 3 1 s s 2 2 Figura 6.1: Esempio di applicazione dell’algoritmo per il calcolo dei cammini di lunghezza minima dal nodo s. La prima figura rappresenta l’inizializzazione (passo 1), mentre ciacuna delle figure successive corrisponde ad un’iterazione del ciclo 2. In ciascuna di queste: i numeri accanto ai nodi rappresentano le distanze d(v) calcolate dall’algoritmo; i nodi in R sono precisamente quelli con un’etichetta diversa da +∞; gli archi in grassetto vanno da p(v) a v per ogni v ∈ R ∖ {s}. Si noti che all’ultima iterazione la scelta dell’arco da aggiungere non è univoca: avremmo potuto scegliere l’arco verticale più a destra. Si osservi anche che dopo la terza iterazione è possibile arrestare l’esecuzione dell’algoritmo, in quanto R = V . Al termine dell’algoritmo, gli archi in grassetto costituiscono un albero di cammini di lunghezza minima. minimo da s a v, per qualche k ∈ {1, . . . , n − 1}. Se u è il predecessore di v in P , allora Psu è un cammino (k − 1)-ristretto di costo minimo da s e u. Dimostrazione. Supponiamo per assurdo che esista un cammino (k − 1)-ristretto Q da s a u tale che c(Q) < c(Psu ). Se Q non contiene v, allora la sequenza Q, (u, v), v definisce un cammino k-ristretto da s a v il cui costo è c(Q) + cuv < c(Psu ) + cuv = c(P ), in contraddizione con il fatto che P è un cammino k-ristretto di costo minimo da s a v. Supponiamo dunque che Q contenga v. Allora la sequenza Qvu , (u, v), v definisce un ciclo C. Poiché P è un cammino k-ristretto di costo minimo da s a v e Qsv ha meno di k archi, c(P ) ≤ c(Qsv ). Dunque c(C) = c(Qvu ) + cuv = (c(Q) − c(Qsv )) + (c(P ) − c(Psu )) = (c(Q) − c(Psu )) + (c(P ) − c(Qsv )) < 0 + 0. Pertanto C è un ciclo di costo negativo, contro le ipotesi. ◻ La proposizione precedente implica che se D non contiene cicli di costo negativo, allora, per ogni v ∈ V e k ∈ {1, . . . , n − 1}, ℓk (v) = min {ℓk−1 (u) + cuv }. u∶(u,v)∈A (6.2) 68 CAPITOLO 6. PROBLEMI DI CAMMINO MINIMO Osserviamo inoltre che vale l’ovvia proprietà ℓk (v) ≤ ℓk−1 (v). Il seguente risultato rispecchia in qualche modo il Lemma 6.2 per il caso dei cammini di lunghezza minima. Lemma 6.6 Sia D = (V, A) un grafo orientato, siano s, v ∈ V e siano (ce )e∈A costi tali che D non contenga alcun ciclo di costo negativo. Fissato k ∈ {1, . . . , n − 1}, supponiamo che ℓk (v) < ℓk−1 (v). Sia u un nodo che realizza il minimo in (6.2), cioè (u, v) ∈ A e ℓk (v) = ℓk−1 (u) + cuv . Allora, se Q è un qualunque cammino (k − 1)-ristretto di costo minimo da s a u, la sequenza Q, (u, v), v costituisce un cammino k-ristretto di costo minimo da s a v. Dimostrazione. Se Q non contiene v, allora la sequenza Q, (u, v), v costituisce un cammino k-ristretto da s a v il cui costo è c(Q) + cuv = ℓk−1 (u) + cuv = ℓk (v), quindi è un cammino k-ristretto di costo minimo da s a v. Basta dunque dimostrare che Q effettivamente non contiene v. Se Q contenesse v, allora la sequenza Qvu , (u, v), v definirebbe un ciclo C, il cui costo sarebbe c(C) = c(Qvu ) + cuv = (c(Q) − c(Qsv )) + (ℓk (v) − ℓk−1 (u)) = (c(Q) − ℓk−1 (u)) + (ℓk (v) − c(Qsv )) < 0 + (ℓk−1 (v) − c(Qsv )) ≤ 0, una contraddizione. ◻ Si tenga presente che il risultato del lemma precedente potrebbe non valere se ℓk (v) = ℓk−1 (v). (Tuttavia, si vede subito dalla dimostrazione che se D non contenesse né cicli di costo negativo né cicli di costo nullo allora il risultato varrebbe anche quando ℓk (v) = ℓk−1 (v).) Diamo ora una descrizione formale dell’algoritmo di Bellman–Ford. Come nel caso dell’algoritmo per cammini di lunghezza minima visto in precedenza, manterremo un predecessore p(v) di ogni nodo v ∈ V ∖ {s}, dove p(v) sarà il nodo che precede v in un cammino di costo minimo da s a v. Grazie al Lemma 6.6, tramite i p(v) sarà possibile ricostruire tutti i cammini minimi con sorgente s. Algoritmo di Bellman–Ford Input: Un grafo orientato D = (V, A), un nodo s ∈ V e costi (ce )e∈A tali che D non contenga cicli di costo negativo. Output: Per ogni v ∈ V , il costo ℓ(v) di un cammino minimo da s a v ed un nodo p(v) che precede v in un tale cammino (se ℓ(v) < +∞; altrimenti p(v) non è definito). 1. Si ponga ℓ0 (s) ∶= 0 e ℓ0 (v) ∶= +∞ per ogni v ∈ V ∖ {s}; 2. Per k = 1, . . . , ∣V ∣ − 1: 2.1 Si ponga ℓk (v) ∶= ℓk−1 (v) per ogni v ∈ V ; 2.2 Per ogni (u, v) ∈ A: Se ℓk (v) > ℓk−1 (u) + cuv , si aggiornino ℓk (v) ∶= ℓk−1 (u) + cuv e p(v) ∶= u; 3. Si ponga ℓ(v) ∶= ℓ∣V ∣−1 (v) per ogni v ∈ V . 69 6.2. L’ALGORITMO DI BELLMAN–FORD Teorema 6.7 L’algoritmo di Bellman–Ford funziona correttamente ed esegue O(∣V ∣ ⋅ ∣A∣) operazioni. Dimostrazione. Per dimostrare la correttezza dell’algoritmo, è sufficiente osservare che, alla fine della k-esima iterazione, ℓk (v) è esattamente il costo minimo di un cammino k-ristretto da s a v. Questo può essere facilmente dimostrato per induzione, osservando che la proprietà è chiaramente vera per ℓ0 dopo l’esecuzione del passo 1 e che si mantiene vera grazie alla formula (6.2). Poiché ogni cammino in D ha al massimo ∣V ∣ − 1 archi, l’istruzione 3 definisce ℓ(v) come il costo minimo tra tutti i cammini da s a v. Per verificare che i puntatori p(v) permettono di ricostruire a ritroso i cammini minimi, osserviamo che se ℓk (v) = ℓk−1 (v), allora la k-esima iterazione dell’algoritmo non aggiorna il predecessore p(v) (e questa scelta è corretta, perché il cammino (k − 1)-ristretto di costo minimo già trovato è anche un cammino k-ristretto di costo minimo); in caso contrario, detto u un nodo che realizza il minimo in (6.2), l’algoritmo pone p(v) = u. Il Lemma 6.6 garantisce che questa scelta è corretta. Per quanto riguarda la complessità, l’algoritmo compie ∣V ∣ − 1 iterazioni, una per ogni valore di k. Ad ogni iterazione, vengono considerati uno alla volta tutti gli archi e, per ciascuno di essi, viene eseguito un confronto ed eventualmente un aggiornamento di ℓk (v) e p(v). Nel complesso, vengono svolte O(∣V ∣ ⋅ ∣A∣) operazioni. ◻ Sia R l’insieme dei nodi raggiungibili da s. Al termine dell’esecuzione dell’algoritmo di Bellman–Ford, ℓ(v) < +∞ esattamente per i nodi v ∈ R; inoltre, per tali nodi p(v) è definito. Come nel caso del problema dei cammini di lunghezza minima, il sottografo T = (R, A′ ), dove A′ = {(p(v), v) ∶ v ∈ R ∖ {s}}, è un’arborescenza con radice s. Per ogni v ∈ R, l’unico cammino da s a v in T è un cammino di costo minimo da s a v in D. Si dice che T è un albero di cammini di costo minimo. Esempio 6.8 Risolvere il problema dei cammini minimi a partire dal nodo s nel grafo in figura (i numeri vicino agli archi ne indicano il costo). a d 1 3 −2 s −2 c 3 1 −1 2 2 b 2 e Il grafo assegnato non contiene alcun ciclo di costo negativo: per il momento dobbiamo accontentarci di una verifica “a occhio”, ma vedremo nella Sezione 6.4 come controllare questa proprietà in modo efficiente. Possiamo dunque applicare l’algoritmo di Bellman–Ford. Rappresentiamo le iterazioni dell’algoritmo attraverso una tabella, con una riga per ogni iterazione e una colonna per ogni nodo. Nella posizione situata nella riga relativa all’iterazione k e nella colonna relativa al nodo v, scriviamo il valore di ℓk (v) ed il nodo p(v) ottenuti alla fine della k-esima iterazione. La riga k = 0 corrisponde all’inizializzazione (passo 1 dell’algoritmo). Poiché il grafo ha sei nodi, l’algoritmo eseguirà cinque iterazioni. 70 CAPITOLO 6. PROBLEMI DI CAMMINO MINIMO Iterazione k=0 k=1 k=2 k=3 k=4 k=5 s 0 0 0 0 0 0 (−) (−) (−) (−) (−) (−) a b c d e ∞ (−) 3 (s) 3 (s) 3 (s) 3 (s) 3 (s) ∞ (−) 2 (s) 1 (a) 1 (a) 1 (a) 1 (a) ∞ (−) ∞ (−) 1 (a) 1 (a) 1 (a) 1 (a) ∞ (−) ∞ (−) ∞ (−) 2 (c) 2 (c) 2 (c) ∞ (−) ∞ (−) 4 (b) 3 (b) 1 (d) 1 (d) Come si evince dalla dimostrazione del Teorema 6.7, alla fine della k-esima iterazione la tabella fornisce i cammini minimi k-ristretti. Si noti che dopo l’iterazione k = 4 non viene fatto alcun aggiornamento. Questo vuol dire che tutti i cammini di costo minimo trovati hanno lunghezza inferiore o uguale a 4. L’ultima riga della tabella permette di costruire un albero di cammini di costo minimo, come rappresentato in figura. a d 1 3 −2 s −2 c 3 1 −1 2 2 b 6.3 2 e Cammini minimi e programmazione lineare In questa sezione mostriamo come la programmazione lineare possa essere utilizzata per risolvere il problema di trovare il cammino di costo minimo tra due nodi fissati. Sarà utile il seguente risultato. Ricordiamo che indichiamo con ℓ(v) il costo di un cammino minimo da s a v. Proposizione 6.9 Sia D = (V, A) un grafo orientato con costi (ce )e∈A tali che D non contenga alcun ciclo orientato di costo negativo. Fissiamo s ∈ V tale che ogni nodo v ∈ V sia raggiungibile da s. Allora valgono le condizioni di Bellman: ℓ(v) − ℓ(u) ≤ cuv per ogni (u, v) ∈ A. (6.3) Dimostrazione. Supponiamo per assurdo che esista un arco (u, v) ∈ A tale che ℓ(v)−ℓ(u) > cuv . Sia P un cammino di costo minimo da s a u. Se v ∉ V (P ), allora la sequenza P, (u, v), v forma un cammino da s a v di costo ℓ(u) + cuv < ℓ(v), una contraddizione. Dunque possiamo assumere che v sia un nodo di P . Si noti che il sottocammino Psv ha costo almeno ℓ(v). Allora la sequenza Pvu , (u, v), v definisce un ciclo C in D per cui vale c(C) = c(P ) − c(Psv ) + cuv ≤ ℓ(u) − ℓ(v) + cuv < 0, una contraddizione. ◻ Fissiamo un grafo orientato D = (V, A) con costi (ce )e∈A sugli archi e due nodi s, t ∈ V . Definiamo una variabile xe per ogni arco e ∈ A. Ad ogni cammino P da s a t associamo il vettore x ∈ RA cosı̀ definito: ⎧ ⎪ ⎪1 se e ∈ A(P ) xe = ⎨ (6.4) ⎪0 altrimenti. ⎪ ⎩ 6.3. CAMMINI MINIMI E PROGRAMMAZIONE LINEARE 71 Ogni cammino P da s a t contiene esattamente un arco di δ+ (s) e nessun arco di δ− (s), mentre contiene esattamente un arco di δ− (t) e nessun arco di δ+ (t); inoltre, per ogni vertice v ∈ V (P ) ∖ {s, t}, P contiene esattamente un arco di δ+ (v) e un arco di δ− (v); infine, per ogni v ∈ V ∖ V (P ), P non contiene archi né di δ+ (v) né di δ− (v). Allora, affinché x ∈ RA sia un vettore corrispondente ad un cammino da s a t, dovranno essere soddisfatti i vincoli ⎧ −1 se v = s ⎪ ⎪ ⎪ ⎪ ∑ xe − ∑ xe = ⎨+1 se v = t ⎪ ⎪ e∈δ− (v) e∈δ+ (v) ⎪ ⎪ ⎩ 0 se v ∈ V ∖ {s, t}. (6.5) Inoltre, dovrà valere xe ≥ 0 per ogni e ∈ A. Se x è associato ad un cammino P da s a t, il costo di P può essere espresso come ∑e∈A ce xe . Consideriamo dunque il seguente programma lineare: min ∑ ce xe (6.6) e∈A s.a ∑ xe − ∑ xe = bv , v ∈ V, e∈δ− (v) (6.7) e∈δ+ (v) xe ≥ 0, e ∈ A, (6.8) dove, per scrivere le condizioni (6.5) in modo compatto, abbiamo posto bs = −1, bt = 1 e bv = 0 per ogni v ∈ V ∖ {s, t}. La discussione precedente implica la seguente osservazione. Osservazione 6.10 Sia P un cammino da s a t in D e sia x il corrispondente vettore definito come in (6.4). Allora x è una soluzione ammissibile per il programma lineare (6.6)–(6.8) e ha costo c(P ). Il viceversa non è vero: ci sono soluzioni ammissibili di (6.6)–(6.8) le cui componenti sono diverse da 0 e 1 e che dunque non sono associate ad alcun cammino. Vedremo, tuttavia, che il valore ottimo del programma lineare (6.6)–(6.8) è esattamente il costo di un cammino minimo da s a t quando il grafo non contiene cicli di costo negativo. Scriviamo il duale di (6.6)–(6.8). Poiché c’è una variabile per ogni arco ed un vincolo per ogni nodo del grafo, il duale avrà un vincolo per ogni arco e una variabile yv per ogni nodo v ∈ V . Per ogni arco (u, v) ∈ A, la variabile xuv ha coefficiente +1 nel vincolo (6.7) corrispondente al nodo v e coefficiente −1 nel vincolo corrispondente al nodo u, mentre ha coefficiente 0 in tutti gli altri vincoli (6.7). Pertanto, il duale è il seguente: max yt − ys s.a yv − yu ≤ cuv , (6.9) (u, v) ∈ A. (6.10) Si noti che i vincoli del problema duale dipendono unicamente da D e da c, ma non dalla scelta dei nodi s e t. Una soluzione ammissibile y del duale è chiamata un potenziale ammissibile per (D, c). Teorema 6.11 Sia D = (V, A) un grafo orientato con costi (ce )e∈A sugli archi. Esiste un potenziale ammissibile per (D, c) se e solo se D non contiene alcun ciclo di costo negativo rispetto a c. 72 CAPITOLO 6. PROBLEMI DI CAMMINO MINIMO Dimostrazione. Per il Lemma di Farkas (Teorema 4.13), esiste un potenziale ammissibile per (D, c) se e solo se non esiste alcun x ∈ RA tale che ∑ ce xe <0 (6.11) e∈A ∑ xe − ∑ xe = 0, v ∈ V e∈δ− (v) (6.12) e∈δ+ (v) xe ≥ 0, e ∈ A. (6.13) Basta dunque dimostrare che D contiene un ciclo di costo negativo se e solo il sistema (6.11)– (6.13) ha una soluzione. Supponiamo che D contenga un ciclo di costo negativo C e sia x̄ il vettore definito da ⎧ ⎪ ⎪1 se e ∈ A(C) x̄e = ⎨ ⎪ ⎪ ⎩0 altrimenti. (6.14) Allora x̄ è una soluzione del sistema (6.11)–(6.13). Viceversa, supponiamo che il sistema abbia una soluzione x̄ e scegliamo x̄ col numero minimo di componenti positive. Definiamo S = {e ∈ A ∶ x̄e > 0}. Si noti che S ≠ ∅, altrimenti x̄ non soddisferebbe la disequazione (6.11). Allora esiste un arco (v0 , v1 ) ∈ S. Siccome ∑e∈δ− (v1 ) x̄e > 0, per la (6.12) esiste (v1 , v2 ) ∈ S. Procedendo in questo modo, costruiamo una sequenza di archi (v0 , v1 ), (v1 , v2 ), (v2 , v3 ), . . . tutti in S. Poiché il numero di nodi è finito, prima o poi troveremo un nodo vk già visitato, cioè chiuderemo un ciclo C tale che A(C) ⊆ S. Sia ε = min{x̄e ∶ e ∈ A(C)}. Se definiamo un vettore x′ ponendo ⎧ ⎪ ⎪x̄e − ε x′e = ⎨ ⎪ ⎪ ⎩x̄e se e ∈ A(C) altrimenti (6.15) si verifica facilmente che x′ soddisfa le condizioni (6.12)–(6.13). Se c(C) < 0, abbiamo trovato il ciclo che cercavamo; dunque assumiamo c(C) ≥ 0. Allora ∑ ce xe = ∑ ce x̄e − ε ⋅ c(C) ≤ ∑ ce x̄e < 0, ′ e∈A e∈A e∈A dunque x′ soddisfa tutte le condizioni (6.11)–(6.13). Per come è stato definito ε, x′ ha meno componenti positive di x̄, il che contraddice la scelta di x̄. ◻ Il Teorema 6.13, enunciato più sotto, mostra che in assenza di cicli di costo negativo il programma lineare (6.6)–(6.8) permette di determinare il costo di un cammino minimo da s a t. Prima però dobbiamo dimostrare un lemma preliminare. Lemma 6.12 Sia D = (V, A) un grafo orientato con costi (ce )e∈A sugli archi e siano s, t ∈ V due nodi fissati. Sia R l’insieme dei nodi raggiungibili da s e supponiamo che t ∈ R. Se il programma lineare (6.6)–(6.8) associato a D ha una soluzione ottima, allora anche il programma lineare (6.6)–(6.8) associato al sottografo di D indotto da R ha una soluzione ottima; inoltre, i valori ottimi coincidono. Dimostrazione. Indichiamo con D ′ il sottografo di D indotto da R e con A′ l’insieme degli archi di D ′ ; dunque D ′ = (R, A′ ). Chiamiamo PL(D) e PL(D ′ ) i programmi lineari della 6.3. CAMMINI MINIMI E PROGRAMMAZIONE LINEARE 73 forma (6.6)–(6.8) associati rispettivamente a D e D ′ . PL(D ′ ) si ottiene da PL(D) ponendo a zero le variabili xe con e ∈ A ∖ A′ . Dunque PL(D ′ ) è più vincolato di PL(D). Allora sarà sufficiente dimostrare che ogni soluzione ammissibile PL(D) (in particolare la soluzione ottima) è ammissibile anche per PL(D ′ ). Sia x̄ ∈ RA una soluzione ammissibile PL(D). Definiamo δ+ (R) = {(u, v) ∈ A ∶ u ∈ R, v ∉ R}, δ− (R) = {(u, v) ∈ A ∶ u ∉ R, v ∈ R}. Consideriamo la somma delle equazioni (6.7) di PL(D) corrispondenti ai nodi v ∈ R. Se e è un arco con entrambi gli estremi in R, la variabile xe appare una volta con coefficiente +1 e una volta con coefficiente −1; dunque queste variabili non appaiono nella somma. Otteniamo allora (6.16) ∑ x̄e − ∑ x̄e = 0 e∈δ− (R) e∈δ+ (R) (il termine noto è 0 perché s, t ∈ R). Si noti che δ+ (R) = ∅: infatti, se esistesse (u, v) ∈ A con u ∈ R e v ∉ R, allora, poiché u è raggiungibile da s, anche v lo sarebbe, contraddicendo il fatto che v ∉ R. Allora, per la (6.16), ∑e∈δ− (R) x̄e = 0, da cui, poiché x̄ ≥ 0, si deduce che x̄e = 0 per ogni e ∈ δ− (R). Poiché A ∖ A′ = δ+ (R) ∪ δ− (R), abbiamo mostrato che x̄e = 0 per ogni e ∈ A ∖ A′ , da cui segue che x̄ è ammissibile per PL(D ′ ). ◻ Teorema 6.13 Sia D = (V, A) un grafo orientato con costi (ce )e∈A sugli archi e siano s, t ∈ V due nodi fissati. Allora: (i) il programma lineare (6.6)–(6.8) è inammissibile se e solo se non esiste alcun cammino da s a t in D; (ii) se il programma lineare (6.6)–(6.8) è illimitato, D ha un ciclo di costo negativo; (iii) se il programma lineare (6.6)–(6.8) ha soluzione ottima, il valore ottimo è il costo di un cammino minimo da s a t in D. Dimostrazione. Mostriamo prima la (i). Supponiamo che (6.6)–(6.8) abbia una soluzione ammissibile x̄ e scegliamo x̄ col numero minimo di componenti positive. Sia S = {e ∈ A ∶ x̄e > 0}. Supponiamo prima che S contenga un ciclo C e sia ε = min{x̄e ∶ e ∈ A(C)}. Il vettore x′ definito come in (6.15) è ammissibile e ha meno componenti positive di x̄, una contraddizione. Dunque S non contiene cicli. Ora, l’equazione (6.12) per v = s garantisce l’esistenza di un arco (s, v1 ) ∈ S. Se v1 ≠ t, l’equazione (6.12) per v = v1 garantisce l’esistenza di un arco (v1 , v2 ) ∈ S. Poiché S non contiene cicli, v2 ≠ s. Se v2 ≠ t, troviamo (v2 , v3 ) ∈ S e cosı̀ via. Siccome S non contiene cicli, il processo termina solo quando si trova un nodo vk = t: ma allora abbiamo individuato un cammino da s a t in S, e dunque in D. Questo mostra che se (6.6)–(6.8) ha soluzioni ammissibili allora esiste un cammino da s a t in D. Il viceversa segue dall’Osservazione 6.10. Per dimostrare la (ii), ricordiamo che se (6.6)–(6.8) è illimitato allora il suo duale (6.9)– (6.10) è inammissibile. Questo significa che non ci sono potenziali ammissibili in (D, c). Per il Teorema 6.11, D ha un ciclo di costo negativo. Dimostriamo ora la (iii). Sia x̄ una soluzione ottima del programma lineare. Per il Lemma 6.12, possiamo restringerci al sottografo indotto da R; per non appesantire la notazione, 74 CAPITOLO 6. PROBLEMI DI CAMMINO MINIMO assumiamo senza perdita di generalità che R = V , dunque lavoriamo direttamente sul programma lineare originale. Poiché R = V , per ogni v ∈ V il costo di un cammino minimo da s a v è un valore ℓ(v) < +∞. Definiamo ȳv = ℓ(v). Poiché valgono le condizioni di Bellman (6.3), ȳ è una soluzione ammissibile del problema duale (6.9)–(6.10). Inoltre, poiché ȳs = ℓ(s) = 0, il costo di ȳ è pari a ℓ(t). Allora, per il Teorema di Dualità Debole, il costo di x̄ è almeno ℓ(t), cioè il valore ottimo del programma lineare è almeno ℓ(t). D’altro canto, detto P un cammino minimo da s a t (dunque c(P ) = ℓ(t)), sappiamo dall’Osservazione 6.10 che il programma lineare ha una soluzione ammissibile di costo ℓ(t). Dunque il valore ottimo del programma lineare è proprio ℓ(t). ◻ In particolare, se D non ha cicli di costo negativo, allora il programma lineare (6.6)–(6.8) permette di determinare il costo di un cammino minimo da s a t. Si noti però che conoscere il costo di un cammino minimo non significa conoscere anche un cammino minimo. Tuttavia, è possibile dimostrare (ma non lo facciamo), che se x∗ è una soluzione ottima di base, allora tutte le componenti di x∗ sono 0 o 1, dunque x∗ corrisponde ad un cammino minimo P secondo la relazione (6.4). Poiché il metodo del simplesso restituisce sempre una soluzione ottima di base (quando il problema ammette ottimo), tale metodo può essere usato per trovare un cammino di costo minimo (in assenza di cicli di costo negativo). Osserviamo anche che nella parte (iii) abbiamo dimsotrato che il vettore (ℓ(v))v∈V costituisce una soluzione ottima del programma lineare duale. Notiamo infine che l’inversa della proposizione (ii) del Teorema 6.13 non vale: infatti, è facile costruire un grafo orientato D che abbia cicli di costo negativo ma che sia privo di cammini da s a t; in tal caso, il programma lineare è inammissibile. 6.4 Trovare cicli di costo negativo Concludiamo il capitolo mostrando come risolvere il seguente problema: dato un grafo orientato D = (V, A) con costi (ce )e∈A sugli archi, si trovi un ciclo di costo negativo in D o si stabilisca che un tale ciclo non esiste. Questa informazione è necessaria, ad esempio, per sapere se è lecito applicare l’algoritmo di Bellman–Ford (ma ci sono anche altre applicazioni). Costruiamo un grafo orientato D ′ = (V ′ , A′ ) con V ′ = V ∪ {s}, dove s è un nuovo nodo (dunque s ∉ V ), e A′ = A ∪ {(s, v) ∶ v ∈ V }. In altre parole, dal nuovo nodo escono archi verso tutti gli altri nodi del grafo. Definiamo costi (c′e )e∈A′ sugli archi di D ′ ponendo ⎧ ⎪ ⎪ce c′e = ⎨ ⎪ ⎪ ⎩0 per e ∈ A per e ∈ A′ ∖ A. Applichiamo al nuovo grafo l’algoritmo di Bellman–Ford rispetto al nodo sorgente s (questo è possibile anche se non sappiamo se il grafo contenga cicli di costo negativo, in quanto le istruzioni dell’algoritmo sono comunque ben poste). L’algoritmo restituisce valori ℓ(v) per ogni v ∈ V ′ . Si tenga presente che ℓ(v) non è necessariamente il costo di un cammino minimo da s a v, in quanto potremmo aver applicato l’algoritmo ad un grafo contenente cicli di costo negativo (ed in tal caso l’algoritmo non trova correttamente i cammini minimi). Come vedremo, se consideriamo le sole componenti ℓ(v) associate ai nodi originali del grafo (dunque solo per v ≠ s), D non ha cicli di costo negativo se e solo se il vettore (ℓ(v))v∈V soddisfa le condizioni di Bellman (6.3). Otteniamo dunque il seguente algoritmo. 6.4. TROVARE CICLI DI COSTO NEGATIVO 75 Algoritmo per trovare cicli di costo negativo Input: Un grafo orientato D = (V, A) con costi (ce )e∈A sugli archi. Output: L’informazione se c’è un ciclo di costo negativo in D. 1. Si definisca V ′ ∶= V ∪ {s}, A′ ∶= A ∪ {(s, v) ∶ v ∈ V }, D ′ ∶= (V ′ , A′ ), c′e ∶= ce per ogni e ∈ A, c′e ∶= 0 per ogni e ∈ A′ ∖ A. 2. Si applichi l’algoritmo di Bellman–Ford a D ′ . 3. Se il vettore (ℓ(v))v∈V restituito dall’algoritmo di Bellman–Ford soddisfa le condizioni di Bellman (6.3), allora D non ha cicli di costo negativo; altrimenti, D ha un ciclo di costo negativo. Teorema 6.14 L’algoritmo per trovare cicli di costo negativo funziona correttamente ed esegue O(∣V ∣ ⋅ ∣A∣) operazioni. Dimostrazione. Se il vettore (ℓ(v))v∈V soddisfa le condizioni di Bellman (6.3), allora (ℓ(v))v∈V è un potenziale ammissibile per (D, c) e dunque D non contiene alcun ciclo di costo negativo (Teorema 6.11). Mostriamo ora che se invece le condizioni di Bellman non sono soddisfatte da (ℓ(v))v∈V , allora D contiene un ciclo di costo negativo. Se, per assurdo, D non contenesse alcun ciclo di costo negativo rispetto a c, allora D ′ non conterrebbe alcun ciclo di costo negativo rispetto a c′ (perché nessun ciclo di D ′ può contenere il nodo s). Allora, poiché in questo caso l’algoritmo di Bellman–Ford funziona correttamente (Teorema 6.7), avremmo che ℓ(v) è il costo di un cammino minimo da s a v in D ′ . Poiché tutti i nodi di D ′ sono raggiungibili da s, la Proposizione 6.9 implica che valgono le condizioni di Bellman, contro l’ipotesi. Per quanto riguarda la stima sul numero di operazioni, osserviamo che ∣V ′ ∣ = ∣V ∣ + 1 e ′ ∣A ∣ = ∣A∣ + ∣V ∣. Allora, per il Teorema 6.7, il passo 2 richiede O(∣V ′ ∣ ⋅ ∣A′ ∣) = O(∣V ∣ ⋅ ∣A∣) operazioni. I passi 1 e 3 richiedono un numero di operazioni inferiore a O(∣V ∣ ⋅ ∣A∣). ◻ Se l’algoritmo determina che esiste un ciclo di costo negativo, tale ciclo può essere esplicitamente costruito. Infatti, supponiamo che il vettore (ℓ(v))v∈V trovato al punto 2 non soddisfi le condizioni di Bellman e sia (u, v) ∈ A tale che ℓ(v) − ℓ(u) > cuv . Si consideri la sequenza di nodi v, u, p(u), p(p(u)), p(p(p(u))), eccetera, dove p(⋅) indica il nodo predecessore determinato dall’algoritmo di Bellman–Ford. Si può dimostrare che questa sequenza visita uno stesso nodo due volte, senza arrivare al nodo fittizio s; ciò determina un cilco, che ha costo negativo (omettiamo la dimostrazione). Esempio 6.15 Determinare se il grafo rappresentato nella parte sinistra della Figura 6.2 contiene un ciclo di costo negativo. Aggiungiamo al grafo dato un nuovo nodo s, da cui escono archi di costo 0 verso tutti gli altri nodi. Il grafo cosı̀ ottenuto è rappresentato nella parte destra della Figura 6.2. Applichiamo l’algoritmo di Bellman–Ford al nuovo grafo rispetto al nodo sorgente s e compiliamo una tabella come spiegato nell’Esempio 6.8. 76 CAPITOLO 6. PROBLEMI DI CAMMINO MINIMO as sd 1 as −2 1 −2 b c ss −2 s 3 1 −2 c −2 sd 1 −2 s 2 3 s s −3 e b 2 s s −3 e Figura 6.2: Costruzione del grafo D ′ . Gli archi tratteggiati hanno costo 0. Iterazione k=0 k=1 k=2 k=3 k=4 k=5 s 0 0 0 0 0 0 (−) (−) (−) (−) (−) (−) a b c d e ∞ (−) 0 (s) 0 (s) 0 (s) 0 (s) 0 (s) ∞ (−) 0 (s) −2 (a) −2 (a) −2 (a) −2 (a) ∞ (−) 0 (s) −2 (a) −2 (a) −3 (e) −3 (e) ∞ (−) 0 (s) 0 (s) −1 (c) −1 (c) −2 (c) ∞ (−) 0 (s) −3 (b) −5 (b) −5 (b) −5 (b) Le condizioni di Bellman sono violate per l’arco (d, a): infatti, ℓ(a) = 0, ℓ(d) = −2, cda = 1, ma 0 − (−2) = 2 > 1. Dunque esiste un ciclo di costo negativo. Il ciclo può essere determinato seguendo a ritroso la lista dei predecessori a partire dall’arco (d, a), fino a quando si visita due volte uno stesso nodo: a, d, p(d) = c, p(c) = e, p(e) = b, p(b) = a. Questo determina il ciclo che visita i nodi a, b, e, c, d, a, che in effetti ha costo −1.