Capitolo 6
Problemi di flusso su grafo
In questo capitolo introdurremo ed analizzeremo il seguente problema: si vuole trasferire,
a minimo costo, un flusso di beni attraverso una rete in modo da soddisfare la domanda
di certi nodi sulla base di quanto procurato da parte di altri nodi.
Sia G = (V, E) un grafo orientato al quale considereremo associato ad ogni arco e ad
ogni nodo un insieme di grandezze che li caratterizzano. In particolare, assoceremo ad ogni
arco (i, j) ∈ E un costo cij che definisce il costo per unità di flusso nell’arco stesso e che
varia linearmente con il flusso; una capacità uij che indichi la massima quantità di flusso
che può scorrere lungo l’arco; infine, un limite inferiore lij che indica la minima quantità
di flusso che deve scorrere nell’arco medesimo. Ad ogni nodo i ∈ V , invece, assoceremo una
grandezza b(i) che indicherà se il nodo è un punto di rifornimento del flusso oppure è un
punto di domanda del flusso; se b(i) > 0 allora il nodo i sarà un nodo di rifornimento del
flusso, se b(i) < 0 allora il nodo i sarà un nodo di domanda del flusso e se b(i) = 0 allora il
P
nodo i sarà un nodo di trasferimento del flusso. In generale, considereremo ∀i∈V b(i) = 0
e che non ci sia dispersione del flusso lungo gli archi.
Per formulare il nostro problema in termini di modello di ottimizzazione, introduciamo
la variabile decisionale xij che rappresenta la quantità di flusso che scorre nell’arco (i, j).
Il Problema di flusso a costo minimo si può formulare nel seguente modo:
81
82
CAPITOLO 6. PROBLEMI DI FLUSSO SU GRAFO
min
X
cij xij
(i,j)∈E
soggetto ai vincoli:
X
j:(i,j)∈E
xij −
X
xji = b(i)
∀i ∈ V
(6.1)
∀(i, j) ∈ E
(6.2)
j:(j,i)∈E
lij ≤ xij ≤ uij
I vincoli 6.1 impongono sia soddisfatto il bilancio di massa ad ogni nodo. Infatti, essi
indicano che in un nodo la quantità di flusso entrante meno la quantità di flusso uscente
deve eguagliare la quantità di flusso rifornita o richiesta. I vincoli 6.2 impongono invece
che siano rispettati i limiti inferiori e superiori della capacità di ogni arco, cioè che il flusso
che scorre rispetti lo spettro di valori di capacità ammessi. In molte applicazioni si ha
per ogni arco che lij = 0, quindi nel seguito considereremo tale valore, se non altrimenti
specificato.
Ogni vettore X di componenti xij , (i, j) ∈ E è chiamato flusso, ogni vettore che soddisfa
gli insiemi di vincoli 6.1 e 6.2 è detto flusso ammissibile.
6.1
Algoritmi di ricerca del cammino minimo su di un
grafo
Gli algoritmi di ricerca del cammino minimo su un grafo costituiscono uno degli argomenti
principali dello studio delle reti di flusso. Le ragioni risiedono nel gran numero di applicazioni reali che possono essere risolte mediante questo modello, nella grande semplicità
nell’ottenere soluzioni in modo molto efficiente, nel fatto che, nonostante la loro semplicità,
catturano alcuni aspetti teorici che sono alla base delle reti di flusso e della teoria dei grafi
e nel fatto che compaiono come sottoproblemi in molte applicazioni.
6.1. ALGORITMI DI RICERCA DEL CAMMINO MINIMO SU DI UN GRAFO
83
Consideriamo un grafo orientato G = (V, E) al quale associamo ad ogni arco (i, j) ∈ E
una lunghezza cij . Il grafo ha un nodo particolare che chiameremo sorgente s. Definiamo
la lunghezza di un cammino orientato nel grafo come la somma delle lunghezze degli archi
nel cammino. Il problema del cammino minimo in un grafo G = (V, E) (in inglese,
shortest path problem, SPP) consiste nel determinare i cammini di lunghezza minima dalla
sorgente s verso ogni altro nodo in V . In termini di flusso, possiamo modellare il problema
supponendo di inviare, a costo minimo, una unità di flusso verso ogni nodo i ∈ V − {s} in
un grafo senza vincoli di capacità. In termini di formulazione matematica, il modello è il
seguente:
X
min
cij xij
(i,j)∈E
soggetto ai vincoli:
X
xij −
j:(i,j)∈E
xji = n − 1
i=s
(6.3)
xji = −1
∀i ∈ V − {s}
(6.4)
xij ≥ 0
∀(i, j) ∈ E
(6.5)
j:(j,i)∈E
j:(i,j)∈E
X
X
xij −
X
j:(j,i)∈E
Nello studio del cammino minimo dobbiamo fare alcune assunzioni:
• Gli archi devono avere lunghezza intera. Questa assunzione non è restrittiva perché
nella rappresentazione nei computer i numeri irrazionali sono convertiti in numeri
razionali e qualunque numero razionale può essere trasformato in un numero intero
moltiplicandolo per un numero sufficientemente grande.
• Il grafo contiene un cammino orientato dalla sorgente s verso qualunque altro nodo
nel grafo.
• Il grafo non contiene cicli di lunghezza negativa. Per la formulazione vista, l’esistenza
di un ciclo W a costo negativo implicherebbe una soluzione non limitata inferiormente
perché potremmo inviare una quantità infinita di flusso lungo W .
84
CAPITOLO 6. PROBLEMI DI FLUSSO SU GRAFO
P1
p
s
P3
k
P2
Figura 6.1: Cammini da s a k.
Dato che esiste un cammino orientato dalla sorgente ad ogni altro nodo del grafo, allora
possiamo dire che esiste un albero dei cammini minimi che dalla sorgente si emana verso
tutti gli altri nodi i cui archi sono gli archi dei diversi cammini. L’esistenza di tale albero
è sostenuta dalla seguente proprietà:
Proprietà 6.1.1 Se il cammino s = n1 , n2 , . . . , nk = k è un cammino minimo dalla sorgente s al nodo k, allora per ogni p = 2, 3, . . . , k − 1, il sottocammino s = n1 , n2 , . . . , np è
il cammino minimo dalla sorgente s al nodo p.
Dimostrazione: Questa proprietà è facile da dimostrare. Infatti, se prendiamo in Figura 6.1 il cammino minimo P1 -P3 da s a k che passa attraverso un certo nodo p, allora il
cammino P2 non è il cammino minimo da s a p. Se P2 fosse il cammino minimo, allora
sarebbe sufficiente prendere il cammino P2 -P3 da s a k per avere un cammino più corto di
P1 -P3 , contraddicendo la sua ottimalità.
Indichiamo ora con d(i) la distanza del nodo i ∈ V da s. La Proprietà 6.1.1 implica
che se P è un cammino minimo da s a k, allora d(j) = d(i) + cij , ∀(i, j) ∈ P ; anche il
viceversa è vero, infatti:
Proprietà 6.1.2 Se d(j) = d(i)+cij , ∀(i, j) ∈ P , allora P deve essere il cammino minimo
tra s e k.
Dimostrazione:
Per dimostrare questa affermazione,
supponiamo che s
=
n1 , n2 , . . . , nk = k sia la successione di nodi nel cammino P . Allora, prendendo la distanza
d(k) ed aggiungendo e togliendo la distanza di ogni nodo in P otteniamo, manipolando
algebricamente l’espressione, che:
d(k) = d(nk ) = (d(nk ) − d(nk−1 )) + (d(nk−1 ) − d(nk−2 )) + . . . + (d(n2 ) − d(n1 ))
6.1. ALGORITMI DI RICERCA DEL CAMMINO MINIMO SU DI UN GRAFO
85
Considerando che d(n1 ) = d(s) = 0 e che per assunzione d(j) − d(i) = cij , abbiamo che:
d(k) = cnk−1 nk + cnk−1 nk−2 + . . . + cn1 n2 =
X
cij
∀(i,j)∈P
Di conseguenza, P è un cammino diretto dalla sorgente s a k di lunghezza d(k) e dato
che per assunzione d(k) è la distanza del cammino minimo del nodo k, allora P deve essere
il cammino minimo del nodo k.
Quanto detto si può riassumere nel seguente risultato:
Proprietà 6.1.3 Un cammino diretto P dalla sorgente s al nodo k è un cammino di
lunghezza minima se e solo se d(j) = d(i) + cij , ∀(i, j) ∈ P .
6.1.1
L’algoritmo di Dijkstra
L’algoritmo di Dijkstra è un algoritmo in grado di risolvere il problema della ricerca del
cammino minimo dalla sorgente s a tutti i nodi. L’algoritmo mantiene una etichetta d(i)
ai nodi che rappresentano un upper bound sulla lunghezza del cammino minimo del nodo i.
Ad ogni passo l’algoritmo partiziona i nodi in V in due insiemi: l’insieme dei nodi etichettati permanentemente e l’insieme dei nodi che sono ancora etichettati temporaneamente.
La distanza dei nodi etichettati permanentemente rappresenta la distanza del cammino
minimo dalla sorgente a tali nodi, mentre le etichette temporanee contengono un valore
che può essere maggiore o uguale alla lunghezza del cammino minimo.
L’idea di base dell’algoritmo è quella di partire dalla sorgente e cercare di etichettare
permanentemente i nodi successori. All’inizio, l’algoritmo pone il valore della distanza
della sorgente a zero ed inizializza le altre distanze ad un valore arbitrariamente alto (per
convenzione, porremo come valore iniziale delle distanze d(i) = +∞, ∀i ∈ V ). Ad ogni
iterazione, l’etichetta del nodo i è il valore della distanza minima lungo un cammino dalla sorgente che contiene, a parte i, solo nodi etichettati permanentemente. L’algoritmo
seleziona il nodo la cui etichetta ha il valore più basso tra quelli etichettati temporaneamente, lo etichetta permanentemente ed aggiorna tutte le etichette dei nodi a lui adiacenti.
L’algoritmo termina quando tutti i nodi sono stati etichettati permanentemente.
86
CAPITOLO 6. PROBLEMI DI FLUSSO SU GRAFO
In Figura 6.2 è riportata la descrizione dell’Algoritmo di Dijkstra. Le operazioni com-
piute dall’algoritmo sono fondamentalmente due: una operazione di selezione del nodo ed
una operazione di aggiornamento delle distanze. La prima seleziona ad ogni passo il nodo
con il valore dell’etichetta più basso, l’altra verifica la condizione d(j) > d(i) + cij e, in
caso positivo, aggiorna il valore dell’etichetta ponendo d(j) = d(i) + cij .
algorithm DIJKSTRA;
begin
S = ∅;
S =V;
∀i ∈ V , d(i) = ∞;
d(s) = 0;
pred(s) = 0;
while |S| < n do
begin
sia i ∈ S un nodo per cui d(i) = min{d(j) : j ∈ S};
S = S ∪ {i};
S = S − {i};
for ∀(i, j) ∈ A(i) do
if d(j) > d(i) + cij then
begin
d(j) = d(i) + cij ;
pred(j) = i;
end;
end;
end;
Figura 6.2: L’algoritmo di Dijkstra
Vediamo un esempio per analizzare i passi effettuati dall’algoritmo.
Esempio 6.1.1 Dato il grafo G in Figura 6.3, calcolare i cammini minimi del nodo sorgente {1} verso gli altri nodi utilizzando l’algoritmo di Dijkstra. Usare le tabelle di Etichetta
nodo, Archi e Predecessore per indicare rispettivamente l’aggiornamento delle etichette dei
nodi, gli archi candidati per ogni nodo ed i predecessori lungo il cammino minimo.
All’inizio, l’algoritmo partiziona i nodi in due insiemi S (nodi etichettati permanentemente) e S (nodi etichettati temporaneamente), pone l’etichetta della sorgente uguale a zero e
6.1. ALGORITMI DI RICERCA DEL CAMMINO MINIMO SU DI UN GRAFO
2
2
2
4
1
2
4
3
1
3
1
4
5
2
6
1
4
2
5
87
8
3
7
Figura 6.3: Grafo per l’Esempio 6.1.1 e corrispondente tabella degli archi.
le etichette degli altri nodi uguali a ∞. Successivamente entra nel ciclo while ed esegue i
seguenti passi:
• seleziona il primo nodo ad etichetta minima, cioè la sorgente s; quindi, per ogni suo
nodo adiacente ad esso, verifica se d(2) > d(1) + c12 , d(3) > d(1) + c13 e d(4) >
d(1) + c14 ; dato che ∞ > 0 + 2 = 2, ∞ > 0 + 4 = 4 e ∞ > 0 + 2 = 2, tutte e tre le
condizioni sono verificate e quindi aggiorno le etichette ed i predecessori nel seguente
modo: d(2) = d(1) + c12 = 0 + 2 = 2 e pred(2) = 1, d(3) = d(1) + c13 = 0 + 4 = 4 e
pred(3) = 1 ed infine d(4) = d(1) + c14 = 0 + 2 = 2 e pred(4) = 1.
• al secondo passo, l’etichetta con il valore più basso è d(2) e quindi seleziono il nodo 2
che diventa etichettato permanentemente. Per i suoi nodi adiacenti si deve verificare
se d(3) > d(2) + c23 e d(5) > d(2) + c25 ; dato che solo la seconda viene verificata
(perché d(3) = 4 > d(2) + c23 = 2 + 2 = 4 non è verificata, mentre è vero che
d(5) = ∞ > d(2) + c25 = 2 + 4 = 6), aggiorno la sua etichetta ponendo d(5) =
d(2) + c25 = 2 + 4 = 6 e pred(5) = 2.
• al terzo passo, l’etichetta di valore più basso è d(4) e quindi seleziono il nodo 4 che
diventa etichettato permanentemente. Per i suoi nodi adiacenti si deve verificare se
d(3) > d(4) + c43 e d(7) > d(4) + c47 ; dato che entrambe sono verificate (perché
d(3) = 4 > d(4) + c43 = 2 + 1 = 3 e d(7) = ∞ > d(4) + c47 = 2 + 5 = 7), aggiorno le
etichette ed ottengo d(3) = 3 e pred(3) = 4, e d(7) = 7 e pred(7) = 4.
88
CAPITOLO 6. PROBLEMI DI FLUSSO SU GRAFO
• al quarto passo, l’etichetta di valore più basso è d(3) e quindi seleziono il nodo 3 che
diventa etichettato permanentemente. Per i suoi nodi adiacenti si deve verificare se
d(5) > d(3) + c35 e d(6) > d(3) + c36 ; dato che è verificata solo la seconda, aggiorno
l’etichetta corrispondente ed ottengo d(6) = 4 e pred(6) = 3.
• al quinto passo, l’etichetta di valore più basso è d(6) e quindi seleziono il nodo 6 che
diventa etichettato permanentemente. Per i suoi nodi adiacenti si deve verificare se
d(7) > d(6) + c68 e d(8) > d(6) + c68 ; dato che entrambe sono verificate, aggiorno le
due etichette ed ottengo d(7) = 6 e pred(7) = 6, e d(8) = 8 e pred(8) = 6.
• al sesto passo, l’etichetta di valore più basso è d(5) e quindi seleziono il nodo 5 che
diventa etichettato permanentemente. Per i suoi nodi adiacenti si deve verificare se
d(6) > d(5) + c56 e d(8) > d(5) + c58 ; dato che solo la seconda è verificata, aggiorno
l’etichetta corrispondente ed ottengo d(8) = 7 e pred(8) = 5.
• al settimo passo, l’etichetta di valore più basso è d(7) e quindi seleziono il nodo 7
che diventa etichettato permanentemente. Per i suoi nodi adiacenti si deve verificare se d(8) > d(7) + c78 ; dato che la relazione non è verificata non effettuo alcun
aggiornamento.
• all’ottavo ed ultimo passo, l’etichetta di valore più basso è d(8) e quindi seleziono il
nodo 8 che diventa etichettato permanentemente. Il nodo non ha nessun adiacente,
quindi l’algoritmo termina.
Nelle due tabelle in Figura 6.4 sono riassunti rispettivamente l’andamento delle etichette
lungo i singoli passi dell’algoritmo e i predecessori dei singoli nodi per ricostruire i cammini
minimi.
Correttezza dell’algoritmo di Dijkstra
Per dimostrare la correttezza dell’algoritmo di Dijkstra facciamo due ipotesi induttive. La
prima è che le etichette di distanza dei nodi in S sono ottime, la seconda che le etichette di
distanza dei nodi sono la lunghezza del cammino minimo dalla sorgente s che contengono
6.1. ALGORITMI DI RICERCA DEL CAMMINO MINIMO SU DI UN GRAFO
89
Figura 6.4: Evoluzione delle etichette dei nodi e lista di adiacenza per il grafo di Figura 6.3.
solo nodi in S. Per dimostrare la prima ipotesi, ricordiamoci che ad ogni passo l’algoritmo
trasferisce un nodo i con l’etichetta più piccola da S a S; quindi, occorre dimostrare che
d(i) è ottima. Ma per le ipotesi induttive poste, d(i) è la lunghezza del cammino minimo
del nodo i tra tutti i cammini che non contengono un nodo di S. Supponiamo allora, per
assurdo, di considerare un cammino P che contiene almeno un nodo k ∈ S, come mostrato
in Figura 6.5 e di decomporre il cammino P nei due sottocammini P1 -P2 : il sottocammino
P1 non contiene un nodo di S, ma termina in un nodo in S; quindi, per ipotesi induttiva
la lunghezza è d(k) e dato che i ha la distanza minima in S, deve risultare che d(k) ≥ d(i)
e quindi il sottocammino P1 deve avere lunghezza almeno d(i). Dato che ogni arco ha
lunghezza non negativa, la lunghezza del sottocammino P2 è non negativa e quindi la
lunghezza del cammino P è almeno d(i).
Per dimostrare che l’algoritmo verifica la seconda ipotesi induttiva, osserviamo che dopo
che è stata effettuata l’etichettatura permanente del nodo i, le etichette di alcuni nodi in
S − {i} decrescono, perché questo è interno al cammino minimo che si sta cercando per
tali nodi. Ma dopo aver permanentemente etichettato un nodo i, l’algoritmo esamina ogni
arco (i, j) ∈ A(i) e se d(j) > d(i) + cij , allora avviene l’aggiornamento d(j) = d(i) + cij ,
pred(j) = i. Quindi, dopo l’aggiornamento, per le ipotesi induttive, il cammino dalla
sorgente al nodo j definito dal vettore dei predecessori soddisfa la Proprietà 6.1.3 e quindi
le etichette di distanza di ogni nodo in S − {i} sono la lunghezza del cammino minimo
90
CAPITOLO 6. PROBLEMI DI FLUSSO SU GRAFO
S
S
i
pred(i)
P2
s
k
P1
Figura 6.5: Disegno per la correttezza di Dijkstra
soggette alla restrizione che ogni nodo del cammino deve appartenere ad S ∪ {i}.
Per quanto riguarda la complessità computazionale dell’algoritmo di Dijkstra partiamo
dalle due operazioni di base che l’algoritmo esegue. In particolare, l’operazione di selezione
dei nodi viene eseguita n volte ed ad ogni passo scandisce ogni nodo etichettato temporaneamente, quindi esegue n + (n − 1) + (n − 2) + . . . + 1 = O(n2 ) iterazioni. L’operazione
di aggiornamento delle etichette viene eseguita |A(i)| volte per ogni nodo i, quindi lungo
P
tutta l’esecuzione dell’algoritmo viene eseguita ∀i∈V |A(i)| = m; dato che ogni operazione di aggiornamento richiede O(1), ne segue che l’algoritmo impiega O(m) per aggiornare le etichette. Riassumendo, dato che O(m) < O(n2 ), la complessità computazionale
dell’algoritmo di Dijkstra è O(n2 ).
Il valore di complessità trovato è il migliore che si può ottenere per grafi molto densi,
mentre può essere migliorato nel caso di grafi sparsi. Infatti, la complessità della operazione di selezione dei nodi pesa considerevolmente di più rispetto alla complessità dell’aggiornamento delle etichette. Di conseguenza sono stati effettuati molti sforzi diretti al
miglioramento dell’efficienza di tale operazione e, in particolare, implementando strutture
dati più elaborate, si è riuscito a diminuire la complessità sino a O(m + n log n) nel caso
di liste di nodi memorizzate come liste di Fibonacci. Rimandiamo il lettore a [10] per una
trattazione completa sulle diverse implementazioni.
6.2. PROBLEMI DI MASSIMO FLUSSO SU GRAFO
6.2
91
Problemi di massimo flusso su grafo
Il problema della ricerca del massimo flusso su di un grafo è un problema complementare a
quello della ricerca del cammino minimo. Infatti, alcuni aspetti del problema del flusso a
costo minimo sono catturati dal problema del cammino minimo nel quale sono considerati
i costi ma non le capacità; il problema del massimo flusso, invece, considera le capacità, ma
non i costi. L’unione dei problemi del cammino minimo e del flusso massimo rappresenta
la combinazione di tutti gli ingredienti di base dell’ottimizzazione delle reti di flusso.
Consideriamo un grafo orientato G = (V, E) nel quale ad ogni arco (i, j) ∈ E sia
assegnata una capacità uij ≥ 0 e sia U = max{uij : uij < ∞, ∀(i, j) ∈ E}. Il Problema
del massimo flusso su di un grafo si può enunciare nel seguente modo: in un grafo
orientato con capacità, determinare il massimo flusso che può essere inviato da un nodo
s chiamato sorgente ad un nodo t chiamato pozzo, senza eccedere le capacità dei singoli
archi. Il problema può essere formulato matematicamente nel seguente modo:
max v
soggetto ai vincoli:
X
xij −
xij −
j:(i,j)∈E
i=s
(6.6)
X
xji = 0
∀i ∈ V − {s} − {t}
(6.7)
xji = −v
i=t
(6.8)
∀(i, j) ∈ E
(6.9)
j:(j,i)∈E
j:(i,j)∈E
X
xji = v
j:(j,i)∈E
j:(i,j)∈E
X
X
xij −
X
j:(j,i)∈E
0 ≤ xij ≤ uij
Il vettore x = {xij } che soddisfa le equazioni scritte conterrà il valore del flusso per
ogni arco (i, j) e la variabile v conterrà il valore totale del flusso. Nell’analisi del problema
del massimo flusso poniamo le seguenti assunzioni:
• Tutte le capacità sono interi non negativi.
92
CAPITOLO 6. PROBLEMI DI FLUSSO SU GRAFO
• Il grafo non contiene un cammino diretto dal nodo s al nodo t composto solo da archi
di capacità infinita. Questa assunzione impedisce che una quantità di flusso infinita
possa scorrere lungo un cammino, rendendo il problema illimitato.
• Se il grafo contiene l’arco (i, j) allora contiene anche l’arco (j, i). Questa assunzione
non è restrittiva perché possiamo sempre aggiungere archi a capacità nulla.
• Il grafo non contiene archi multipli.
Per poter sviluppare l’algoritmo necessario per risolvere il nostro problema, abbiamo
bisogno di definire il concetto di grafo residuo G(x) corrispondente ad un flusso assegnato
x. Supponiamo che un arco (i, j) trasporti xij quantità di flusso. Quindi possiamo ancora
inviare sull’arco uij − xij unità di flusso dal nodo i al nodo j; analogamente, potremmo
inviare xij unità di flusso da j ad i per annullare il flusso che scorre in (i, j). Sulla base
di questa osservazione possiamo definire un grafo residuo, rispetto ad un dato flusso x,
sostituendo ogni arco (i, j) con una coppia di archi: un arco (i, j) con capacità residua
rij = uij − xij e un arco (j, i) con capacità residua rji = xji , come si può notare in
Figura 6.6. Il grafo residuo consiste solo negli archi con capacità residua positiva. Dalla
(xij , uij )
i
rij
j
i
uij − xij
j
⇒
j
i
xji
Figura 6.6: Costruzione del grafo residuo.
definizione di grafo residuo che abbiamo dato, ne segue che la capacità residua è composta
da due componenti: una componente che indica la capacità non utilizzata uij − xij ed
una componente xji che indica il flusso corrente. Quindi, possiamo scrivere che rij =
uij − xij + xji .
Nella Sezione 5.3 abbiamo dato la definizione di taglio in un grafo come la partizione
dei suoi nodi in due insiemi, S e S = V − S, usando la notazione [S, S]. Nel seguito di
questo capitolo faremo riferimento ai tagli di tipo s-t, ovvero quei tagli nei quali s ∈ S
6.2. PROBLEMI DI MASSIMO FLUSSO SU GRAFO
G
2
4
6
(1,2)
4
1
1
1
6
1
1
2
2
5
(1,3)
1
2
1
(1,2)
(2,2)
2
1
(2,2)
3
1
G(x)
(1,2)
(1,2)
1
93
5
3
1
Figura 6.7: Un grafo G con un flusso assegnato x ed il corrispondente grafo residuo G(x).
e t ∈ S. Inoltre, definiremo come archi diretti del taglio [S, S] gli archi (i, j) tali per cui
i ∈ S e j ∈ S e con archi inversi del taglio [S, S] gli archi (i, j) tali per cui i ∈ S e j ∈ S;
indicheremo con (S, S) l’insieme degli archi diretti del taglio [S, S] e con (S, S) l’insieme
degli archi inversi del taglio [S, S]. Per esempio, in Figura 6.8 è riportato un taglio s-t
per il grafo G di Figura 6.7, dove [S, S] = {(1, 3), (3, 4), (4, 6)}, (S, S) = {(1, 3), (4, 6)} e
(S, S) = {(3, 4)}.
2
4
6
1
5
3
Figura 6.8: Un taglio s-t per il grafo di Figura 6.7.
Definizione 6.2.1 Si definisce capacità u[S, S] di un taglio s-t la somma delle capacità
degli archi diretti del taglio, ovvero:
u[S, S] =
X
(i,j)∈(S,S)
uij
(6.10)
94
CAPITOLO 6. PROBLEMI DI FLUSSO SU GRAFO
Si definisce taglio minimo il taglio s-t che tra tutti i possibili tagli s-t ha capacità
minima.
Chiaramente, la capacità di un taglio è un limite superiore della quantità massima di
flusso che possiamo inviare dai nodi in S ai nodi in S rispettando i vincoli di capacità
imposti.
Definizione 6.2.2 Si definisce capacità residua r[S, S] di un taglio s-t la somma delle
capacità residue degli archi diretti del taglio, ovvero:
X
r[S, S] =
rij
(6.11)
(i,j)∈(S,S)
Dato un flusso x su di un grafo, per calcolare il flusso attraverso un taglio s-t possiamo
utilizzare il vincolo di bilancio di massa della formulazione, per cui:
v=
X· X
xij −
∀i∈S j:(i,j)∈E
X
¸
xji
j:(j,i)∈E
Questa espressione si può semplificare notando che se (p, q) ∈ E e p ∈ S e q ∈ S,
allora comparirà un xpq nella prima sommatoria all’interno delle parentesi quadre (quando
i = p), ed un −xpq nella seconda (quando j = q). Considerando che nella sommatoria non
compaiono neanche le componenti di x che fanno riferimento ad archi che hanno solo nodi
in S, possiamo scrivere che:
v=
X
X
xij −
(i,j)∈(S,S)
xij
(6.12)
(i,j)∈(S,S)
Questa relazione indica che il flusso dai nodi in S ai nodi in S è uguale al flusso che
da S va in S, meno il flusso che da S va a S e, dato che il primo membro dell’equazione è
esattamente il valore del flusso, abbiamo che esso eguaglia esattamente il valore del flusso
nel taglio. Considerando che xij ≤ uij e che xij ≥ 0, possiamo scrivere che:
v≤
X
(i,j)∈(S,S)
uij
(6.13)
6.2. PROBLEMI DI MASSIMO FLUSSO SU GRAFO
95
Questo risultato ci indica che il valore di un qualunque flusso sul grafo è minore o al più
uguale alla capacità di un su qualunque taglio s-t. Tale risultato è abbastanza intuitivo
perché ogni flusso che scorre da s a t deve attraversare ogni taglio s-t e, quindi, non ne può
eccedere la capacità.
6.2.1
L’algoritmo di Ford e Fulkerson
Per risolvere il problema del massimo flusso in un grafo attraverso l’algoritmo che stiamo
per introdurre abbiamo bisogno di alcune definizioni sul grafo residuo:
Definizione 6.2.3 Si definisce cammino aumentante nel grafo residuo un cammino
diretto dalla sorgente al pozzo e capacità residua δ la minima capacità residua di ogni
arco nel cammino aumentante.
Nel grafo in Figura 6.7, un cammino aumentante nel grafo G(x) è costituito dagli
archi {(1, 2), (2, 4), (4, 3), (3, 5), (5, 6)} e la capacità residua δ = min{r12 , r24 , r43 , r35 , r56 } =
min{1, 1, 1, 2, 1} = 1. Come si può notare, la capacità residua è sempre maggiore di zero;
quindi, non appena un grafo contiene un cammino aumentante, possiamo inviare ulteriore
flusso dalla sorgente al pozzo.
Quest’ultima osservazione ci suggerisce l’algoritmo per risolvere il problema della ricerca
del massimo flusso. Infatti, potremmo iniziare utilizzando le tecniche di ricerca viste nella
Sezione 5.1 per identificare un cammino da s a t nel grafo G(x) partizionando i nodi del
grafo in due insiemi: nodi etichettati (quelli raggiungibili da s) e nodi non etichettati
(quelli non raggiungibili da s). Se alla fine del processo t è etichettato, allora invio la
massima quantità di flusso, pari alla capacità residua sul cammino aumentante trovato;
quindi, cancello tutte le etichette e ripeto la procedura iterativamente. L’algoritmo termina
quando non riesco ad etichettare t, cioè quando non esiste un cammino aumentante dalla
sorgente al pozzo.
Prima di esporre l’algoritmo, si vuole sottolineare che la ricerca di un cammino aumentante nel grafo residuo G(x) corrisponde alla ricerca in G di un cammino dalla sorgente al
pozzo, non necessariamente orientato, con xij < uij per ogni arco diretto nel verso del cammino e con xij > 0 per ogni arco inverso rispetto al verso del cammino ed esiste un cammino
96
CAPITOLO 6. PROBLEMI DI FLUSSO SU GRAFO
aumentante rispetto ad un certo flusso x se e soltanto se esiste un cammino diretto da s a t
in G(x). Se ad un certo passo dell’algoritmo si aggiorna il flusso con un flusso addizionale
δ, allora xij varierà rispettando la definizione di capacità residua (rij = uij − xij + xji ) o
aumentando xij di δ unità o diminuendo xji di δ unità, oppure si avrà una combinazione
convessa delle due possibilità precedenti.
algorithm FORD&FULKERSON;
begin
etichetta il nodo t;
while t è etichettato do
begin
cancella le etichette di tutte i nodi i ∈ V ;
pred(j) = 0, ∀i ∈ V ;
etichetta s e poni LISTA= {s};
while LISTA6= ∅ e t è non etichettato do
begin
rimuovi un nodo i da LISTA;
for (i, j) ∈ G(x) do
if nodo j è non etichettato then;
begin
pred(j) = i;
etichetta j;
aggiungi j a LISTA;
end;
end;
if t è etichettato then
begin
usa le etichette dei predecessori per trovare all’indietro
il cammino aumentante P da s a t;
δ = min{rij , (i, j) ∈ P };
aumenta di δ unità di flusso lungo P e aggiorna G(x);
end;
end;
end;
Figura 6.9: L’algoritmo di Ford e Fulkerson
Le osservazioni fatte fin qui possono essere formalizzate nell’algoritmo di Ford e Fulkerson descritto in Figura 6.9. La correttezza dell’algoritmo segue dal fatto che sono possibili
due casi: o l’algoritmo trova un cammino aumentante dalla sorgente al pozzo, oppure non
6.2. PROBLEMI DI MASSIMO FLUSSO SU GRAFO
97
riesce a trovare alcun cammino. Se si verifica il secondo caso dobbiamo dimostrare che
allora il flusso è ottimo. Supponiamo quindi che ad un certo passo, S sia l’insieme dei nodi
etichettati e S = V − S l’insieme dei nodi non etichettati, con s ∈ S e t ∈ S. Se l’algoritmo
non può etichettare i nodi in S a partire dai nodi in S, allora rij = 0, ∀(i, j) ∈ (S, S);
inoltre, dato che rij = uij − xij + xji , xij ≤ uij e xji ≥ 0, allora la condizione rij = 0
implica che xij = uij per ogni arco (i, j) ∈ (S, S) e xij = 0 per ogni arco (i, j) ∈ (S, S).
Sostituendo questi valori nell’Equazione 6.12 otteniamo:
v=
X
(i,j)∈(S,S)
xij −
X
X
xij =
(i,j)∈(S,S)
uij = u[S, S]
(6.14)
(i,j)∈(S,S)
Questa relazione mostra che il valore del flusso corrente x eguaglia la capacità del taglio
[S, S] e dato che l’Equazione 6.13 implica che x è il flusso massimo e [S, S] è il taglio minimo,
allora abbiamo dimostrato il seguente risultato:
Teorema 6.2.1 Il valore massimo del flusso dalla sorgente s al pozzo t in un grafo con
capacità eguaglia la capacità del minimo taglio s-t.
Il teorema precedente, che chiameremo Teorema del massimo flusso e del minimo taglio ci dice anche che quando l’algoritmo di Ford e Fulkerson termina con il massimo
flusso, contemporaneamente ci fornisce anche il taglio minimo.
Esempio 6.2.1 Dato il grafo in Figura 6.10, calcolare il flusso massimo dalla sorgente
{1} al pozzo {8} utilizzando l’algoritmo di Ford e Fulkerson, disegnando la successione dei
grafi residui. Indicare il taglio minimo corrispondente al flusso trovato.
Nel grafo in Figura 6.10 il flusso è posto inizialmente a xij = 0 per ogni arco; quindi, il
primo grafo residuo coincide con il grafo di partenza.
• Nella prima iterazione l’algoritmo trova il cammino aumentante {(1, 2), (2, 5), (5, 8)},
con δ = min{r12 , r25 , r58 } = min{4, 2, 4} = 2. L’algoritmo esegue l’aumento del flusso
pari a δ = 2 unità ed aggiorna il grafo residuo, disegnato in Figura 6.11.
98
CAPITOLO 6. PROBLEMI DI FLUSSO SU GRAFO
2
2
4
4
2
6
1
5
3
3
3
2
3
3
2
4
5
6
8
4
7
Figura 6.10: Grafo per l’Esempio 6.2.1.
2
2
2
2
2
6
1
3
5
3
2
4
2
3
3
6
3
2
2
5
8
4
7
Figura 6.11: Il grafo residuo dopo la prima iterazione.
6.2. PROBLEMI DI MASSIMO FLUSSO SU GRAFO
• Nella
seconda
iterazione
l’algoritmo
99
trova
il
cammino
{(1, 2), (2, 4), (4, 7), (7, 8)} con δ = min{r12 , r24 , r47 , r78 } = 2.
aumentante
L’algoritmo ese-
gue l’aumento del flusso pari a δ = 2 unità ed aggiorna il grafo residuo, disegnato in
Figura 6.12.
2
2
4
2
2
6
1
2
4
6
3
1
2
2
3
3
2
3
5
5
8
2
2
7
Figura 6.12: Il grafo residuo dopo la seconda iterazione.
• Nella terza iterazione l’algoritmo trova il cammino aumentante {(1, 3), (3, 5), (5, 8)}
con δ = min{r13 , r35 , r58 } = 2. L’algoritmo esegue l’aumento del flusso pari a δ = 2
unità ed aggiorna il grafo residuo, disegnato in Figura 6.13.
2
2
4
4
3
2
3
4
2
2
3
1
5
2
3
1
4
6
2
5
8
2
2
7
Figura 6.13: Il grafo residuo dopo la terza iterazione.
• Nella quarta iterazione l’algoritmo trova il cammino aumentante {(1, 3), (3, 6), (6, 8)}
con δ = min{r13 , r36 , r68 } = 3. L’algoritmo esegue l’aumento del flusso pari a δ = 3
unità ed aggiorna il grafo residuo, disegnato in Figura 6.14.
• Nella quinta iterazione, l’algoritmo non riesce ad etichettare il pozzo e quindi termina.
100
CAPITOLO 6. PROBLEMI DI FLUSSO SU GRAFO
2
2
4
1
4
2
2
2
3
3
1
5
6
5
1
2
3
2
2
4
8
3
3
2
7
Figura 6.14: Il grafo residuo dopo la quarta iterazione.
Il flusso massimo è pari alla somma degli incrementi eseguiti nei singoli passi, cioè
v ? = 2 + 2 + 2 + 3 = 9. Il taglio minimo è riportato in Figura 6.15.
2
2
4
3
4
2
6
1
5
3
3
3
2
4
6
3
2
5
8
4
7
Figura 6.15: Il taglio minimo del grafo di Figura 6.10.
Complessità computazionale dell’algoritmo di Ford e Fulkerson
Per calcolare la complessità computazionale osserviamo che l’algoritmo esegue una ricerca
per trovare un cammino dalla sorgente al pozzo (che sappiamo essere eseguibile, dalla
Sezione 5.1, in O(m) passi) tante volte quanto è possibile eseguire degli aumenti di flusso.
Per tali aumenti, se le capacità sono intere e limitate da U , allora la capacità di un taglio
(s, N − {s}) è al più nU . Di conseguenza, siccome l’algoritmo aumenta il flusso di almeno
una unità ogni iterazione, ne segue che globalmente l’algoritmo esegue O(nmU ) iterazioni.
6.2. PROBLEMI DI MASSIMO FLUSSO SU GRAFO
101
L’algoritmo visto è sicuramente uno dei più semplici per risolvere il problema del massimo flusso, ma il valore di complessità computazionale è legato al valore di U e questo
potrebbe portare a casi nei quali l’algoritmo non risulta essere efficiente (per esempio, se
U = 2n ). Inoltre, in alcuni casi può eseguire tante iterazioni quante indicate dal caso
peggiore. Per esempio, se si considera l’istanza della Figura 6.16, l’algoritmo potrebbe
selezionare i cammini aumentanti 1 − 3 − 2 − 4 e 1 − 2 − 3 − 4, alternativamente, 106 volte,
ogni volta aumentando il flusso di una unità.
2
2
6
6
10
10
1
4
1
106
106
3
10
106 -1
6
1
106 -1
1
1
1
4
106
3
2
106 -1
106 -1
1
1
106 -1
1
1
1
4
1
106 -1
3
Figura 6.16: Istanza patologica per l’algoritmo di Ford e Fulkerson.
Un altro difetto dell’algoritmo risiede nel fatto che ad ogni passo deve essere rieseguito il
processo di etichettatura e, quindi, le informazioni che si generano sui cammini aumentanti
vengono perse ad ogni passo e si devono ricalcolare di nuovo.
In [10] sono riportati diversi algoritmi efficienti per la risoluzione del problema del
massimo flusso che non risentono dei limiti dell’algoritmo di Ford e Fulkerson.
Indice analitico
Albero, 45
Ricoprente, 46
Minimo, 70
Algoritmo, 18
Densità, 6
Distanza, 42
Embedding planare, 53
di Kruskal, 74
Faccia, 54
di ordinamento topologico, 68
Flusso, 81
di Prim, 76
Foresta, 45
di Dijkstra, 85
Formula di Eulero, 58
di Ford e Fulkerson, 95
di ricerca su grafo, 61
Grafo, 3
Aciclico, 43
in ampiezza, 64
Bipartito, 11, 44
in profondità, 66
Complemento, 9
di ricerca su stringa, 20
Completo, 6
Archi, 3
Denso, 6
Multipli, 3
Dimensione di un, 5
Disegno di un, 53
Bridge, 41
Duale, 55
Ciclo, 14
Euleriano, 46
Clique, 6
k-regolare, 8
Componenti connesse, 40
Ordine di un, 5
Connessione, 16
Orientato, 33
Crescita di funzioni, 22
Piano, 53
Combinazione di, 24
Planare, 53
Cut-vertex, 41
Semplice, 3
102
INDICE ANALITICO
Sparso, 6
Insieme indipendente, 9
Isomorfismo, 37
Classi di equivalenza, 40
K-Fattorizzazione, 8
Lemma Handshaking, 7
Lista di adiacenza, 37
103
Taglio, 71
Teorema
del massimo flusso e minimo taglio, 97
di Eulero, 46
di Kuratowski, 59
Trail, 15
Euleriano, 46
Hamiltoniano, 48
Loop, 3
Vertex covering, 10
Matching, 11
Vicinato, 7
Matrice di adiacenza, 35
Walk, 15
Matrice di incidenza, 34
Nodi, 3
Notazione Big-O, 23
Numero cromatico, 13
Ordinamento topologico, 67
Path, 14, 15
Problema
decisionale, 17
del cammino minimo, 83
del flusso massimo, 91
dell’albero ricoprente minimo, 70
di flusso a costo minimo, 81
Regione, 54
Sottografo, 4
Indotto, 4
Ricoprente, 4
104
INDICE ANALITICO
Bibliografia
[1] AA.VV. Kaliningrad Business Guide. http://guide.kaliningrad.net.
[2] B. Bollobas. Modern graph theory. Springer-Verlag, 1998.
[3] K. Steiglitz C. H. Papadimitriou.
Combinatorial optimization: algorithms and
complexity. Prentice Hall, 1982.
[4] N. Christofides. Graph theory, an algorithmic approach. Academic Press, 1975.
[5] R. Diestel. Graph Theory. Springer-Verlag, 2005.
[6] S. Fortin. The graph isomorphism problem. Technical Report TR96-20, Department
of Computer Science, The University of Alberta, Canada, July 1996.
[7] D. Jungnickel. Graphs, network and algorithms. Springer-Verlag, 1999.
[8] F. Maffioli. Elementi di programmazione matematica. Casa Editrice Ambrosiana,
2000.
[9] R. J. Wilson N. L. Biggs, E. K. Lloyd. Graph Theory 1736-1936. Oxford University
Press, 1999.
[10] J. B. Orlin R. K. Ahuja, T. L. Magnanti. Network Flows. Pearson Education, 1993.
[11] K. H. Rosen. Discrete mathematics and its applications. McGraw Hill Text, 1998.
[12] P. Serafini. Ottimizzazione. Zanichelli, 2000.
[13] A. Ventre. Introduzione ai grafi planari. Zanichelli Editore, 1983.
105
106
[14] D. B. West. Introduction to graph theory. Prentice Hall, 2000.
BIBLIOGRAFIA