12.07.2015 Views

Algoritmi di tipo preflow-push - DMI

Algoritmi di tipo preflow-push - DMI

Algoritmi di tipo preflow-push - DMI

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

Capitolo 3<strong>Algoritmi</strong> <strong>preflow</strong>-<strong>push</strong>Il problema fondamentale degli algoritmi augmenting path è costituito dall’onerosaoperazione <strong>di</strong> invio <strong>di</strong> flusso lungo un path, che richiede tempo O(n) nel caso peggiore. Glialgoritmi <strong>preflow</strong>-<strong>push</strong> consentono <strong>di</strong> ovviare a tale inconveniente evitando <strong>di</strong> spe<strong>di</strong>reflusso lungo path, ottenendo un notevole miglioramento nella complessità. Al fine <strong>di</strong>comprendere l’idea alla base degli algoritmi <strong>preflow</strong>-<strong>push</strong>, immaginiamo che la reteresidua rappresenti una rete interconnessa <strong>di</strong> serbatoi e tubi. In particolare i no<strong>di</strong>rappresentano serbatoi, gli archi della rete residua rappresentano condotti flessibili traserbatoi, e la <strong>di</strong>stance label associata ad ogni nodo in<strong>di</strong>ca quanto il serbatoio è in altorelativamente al livello del terreno. In tale sistema vogliamo inviare acqua dal serbatoiosorgente al serbatoio pozzo. Immaginiamo, ancora, che il flusso lungo un arco ammissibilerappresenti acqua che scorre verso il basso. Inizialmente, muoviamo il serbatoio sorgenteverso l’alto in modo tale che l’acqua in esso contenuta scorra verso i serbatoi vicini. Ingenerale, l’acqua scorre verso il basso in <strong>di</strong>rezione del pozzo. Può capitare, tuttavia, cheparte del liquido resti bloccata in un serbatoio che non ha serbatoi vicini posizionati più inbasso relativamente ad esso. Se è così, alziamo il serbatoio in modo che defluisca l’acquain esso accumulata. Prima o poi, non potremo più inviare flusso verso il pozzo.Continuiamo allora ad alzare i serbatoi, in modo tale che l’acqua in eccesso ritorni in<strong>di</strong>etroverso il serbatoio sorgente. Terminiamo tale processo quando tutti i serbatoi <strong>di</strong>versi daquello sorgente e quello pozzo, non hanno liquido in eccesso.32


3.1 Generic <strong>preflow</strong>-<strong>push</strong> algorithmConsideriamo l’esempio mostrato in figura 3.1. Un qualsiasi algoritmo augmenting path,quando applicato a tale problema, in<strong>di</strong>viduerà 5 augmenting path, ognuno <strong>di</strong> lunghezza 6,ed aumenterà <strong>di</strong> una unità <strong>di</strong> flusso lungo ognuno <strong>di</strong> tali path. Osserviamo, tuttavia, chenonostante gli augmenting path con<strong>di</strong>vidano i primi 4 archi, ogni aumento attraverserà tuttii 6 archi.r ijs8 8 8 8tFig. 3.1. Svantaggio degli algoritmi augmenting path.Se avessimo inviato 5 unità <strong>di</strong> flusso dal nodo 1 al nodo 5, e dunque una unità <strong>di</strong> flussolungo ognuno dei <strong>di</strong>fferenti path <strong>di</strong> lunghezza 2, avremmo evitato le ripetitivecomputazioni per l’attraversamento degli archi comuni. L’operazione <strong>di</strong> aumento lungo unpath degli algoritmi augmenting path può essere decomposta nella elementare operazione<strong>di</strong> invio <strong>di</strong> flusso lungo archi in<strong>di</strong>viduali. Dunque l’invio <strong>di</strong> δ unità <strong>di</strong> flusso lungo un path<strong>di</strong> k archi può essere considerato come k invii <strong>di</strong> δ unità <strong>di</strong> flusso lungo ognuno degli archidel path. Ci riferiremo ad ognuno <strong>di</strong> tali invii come operazione <strong>di</strong> <strong>push</strong>. Gli algoritmi<strong>preflow</strong>-<strong>push</strong> inviano flusso lungo archi in<strong>di</strong>viduali anziché lungo augmenting path. Acausa <strong>di</strong> questa caratteristica, tali algoritmi non sod<strong>di</strong>sfano i vincoli <strong>di</strong> bilanciamento <strong>di</strong>massa (1.1b) in fasi interme<strong>di</strong>e. Infatti tali algoritmi consentono al flusso entrante in unnodo <strong>di</strong> eccedere il flusso uscente dallo stesso nodo. Formalmente, definiamo <strong>preflow</strong> unafunzione x : A → R che sod<strong>di</strong>sfa i vincoli <strong>di</strong> capacità (1.1c) e il seguente rilassamento deivincoli (1.1b):33


∑xji{ j:(j , i ) ∈ A}−∑xij{ j:(i,j ) ∈ A}≥0∀i∈ N\ { s,t}.Gli algoritmi <strong>preflow</strong>-<strong>push</strong> mantengono un <strong>preflow</strong> in ogni fase interme<strong>di</strong>a. Per un dato<strong>preflow</strong> x, definiamo l’eccesso in un nodo( i)=∑{ j:(j,i)∈A}−∑e xji xij.{ j:(i,j ) ∈A}i ∈ N comeIn un <strong>preflow</strong>, e( i)≥ 0 ∀i∈ N \ { s,t}. Tuttavia, poiché non esiste flusso uscente dal nodo t,anche e ( t)≥ 0 . Pertanto il nodo s è l’unico nodo con eccesso negativo. Ci riferiremo ad unnodo con eccesso strettamente positivo come nodo attivo; per convenzione, i no<strong>di</strong> sorgentee pozzo non sono mai attivi.Gli algoritmi augmenting path durante l’esecuzione mantengono l’ammissibilità dellasoluzione, ovvero il sod<strong>di</strong>sfacimento dei vincoli (1.1), e convergono verso l’ottimalità;invece gli algoritmi <strong>preflow</strong>-<strong>push</strong> cercano <strong>di</strong> raggiungere l’ammissibilità della soluzione.In un algoritmo <strong>preflow</strong>-<strong>push</strong>, la presenza <strong>di</strong> no<strong>di</strong> attivi in<strong>di</strong>ca l’inammissibilità dellasoluzione corrente. Di conseguenza l’operazione <strong>di</strong> base degli algoritmi è selezionare unnodo attivo e tentare <strong>di</strong> spe<strong>di</strong>re il flusso in eccesso verso no<strong>di</strong> a<strong>di</strong>acenti. Poiché il finedell’algoritmo è inviare flusso al nodo pozzo, il flusso in eccesso in un nodo viene spe<strong>di</strong>toad un nodo a<strong>di</strong>acente che sia più vicino al pozzo <strong>di</strong> quanto lo sia il nodo con eccessopositivo. Come negli algoritmi shortest augmenting path, misuriamo la vicinanza al nodopozzo basandoci sulle <strong>di</strong>stance label; pertanto inviare flusso verso un nodo che sia vicino alpozzo equivale ad inviare flusso su archi ammissibili. Se il nodo attivo che stiamoconsiderando non ha archi ammissibili, incrementiamo la sua <strong>di</strong>stance label al fine <strong>di</strong>creare almeno un arco ammissibile. L’algoritmo termina quando la rete non contiene alcunnodo attivo. Descriviamo ora formalmente l’algoritmo <strong>preflow</strong>-<strong>push</strong>.algorithm <strong>preflow</strong>-<strong>push</strong>beginpreprocess;while esiste un nodo attivo dobeginseleziona un nodo attivo i;<strong>push</strong>/relabel(i);end;end;34


procedure preprocessbeginx := 0;calcola le <strong>di</strong>stance label esatte d(i);x sj := u sj per ogni arco (s, j)∈A(s);d(s) := n;end;procedure <strong>push</strong>/relabel(i)beginif la rete contiene un arco ammissibile (i, j) theninvia δ := min{e(i), r ij } unità <strong>di</strong> flusso dal nodo i al nodo j;else d(i) = min{d(j) +1 : (i, j)∈A(i) e r ij > 0};end;L’operazione <strong>push</strong> spinge δ unità <strong>di</strong> flusso dal nodo i al nodo j, decresce <strong>di</strong> δ unità siae(i) sia r ij ed aumenta <strong>di</strong> δ unità e(j) e r ji . Un’ operazione <strong>push</strong> <strong>di</strong> δ unità su un arco (i, j) èsaturating se δ=r ij ed è non saturating altrimenti. Osserviamo che un <strong>push</strong> non saturatingdal nodo i riduce e(i) a zero. L’operazione relabel aumenta la <strong>di</strong>stance label <strong>di</strong> un nodo, inmodo tale da creare un arco ammissibile su cui l’algoritmo possa eseguire altre operazioni<strong>di</strong> <strong>push</strong>. L’operazione preprocess svolge <strong>di</strong>verse funzioni. Innanzitutto invia flusso dallasorgente verso i no<strong>di</strong> a<strong>di</strong>acenti, in modo tale da rendere attivi i no<strong>di</strong> a<strong>di</strong>acenti alla sorgente.Successivamente, poiché l’operazione precedente satura tutti gli archi uscenti dal nodo s,rendendo gli archi inammissibili, pone d(s)=n, che non violerà la con<strong>di</strong>zione <strong>di</strong> vali<strong>di</strong>tà(2.2). Inoltre, essendo d(s)=n, la proprietà (4.3) implica che la rete residua non contiene<strong>di</strong>rect path dal nodo s al nodo t. Essendo poi le <strong>di</strong>stance label non decrescenti, è garantitoche nelle iterazioni successive la rete residua non conterrà mai un <strong>di</strong>rect path dal nodosorgente al nodo pozzo, pertanto non sarà necessario inviare ulteriore flusso dal nodo s. Perillustrare l’algoritmo generic <strong>preflow</strong>-<strong>push</strong>, consideriamo l’esempio dato in figura 3.2(a);in figura 3.2(b) è possibile notare la rete residua dopo l’operazione <strong>di</strong> preprocess.Supponiamo che l’algoritmo selezioni il nodo 2 per l’operazione <strong>di</strong> <strong>push</strong>/relabel. L’arco (2,4) è il solo arco ammissibile e l’algoritmo esegue un’operazione <strong>di</strong> <strong>push</strong> (saturating) delvalore δ =min {e(2), r 24 }=min{2, 1}=1. La Figura 3.2(c) rappresenta la rete residua inquesta fase. Supponiamo che l’algoritmo selezioni ancora il nodo 2. Poiché non esistono35


figura 3.2(f). Ora la rete residua non contiene alcun nodo attivo e l’algoritmo termina. Ilvalore del flusso massimo nella rete è e(4)=6.Assumendo che l’algoritmo generic <strong>preflow</strong>-<strong>push</strong> termini, possiamo mostraresemplicemente che esso in<strong>di</strong>vidua un massimo flusso. L’algoritmo termina quando glieccessi risiedono solo nel nodo sorgente o nel nodo pozzo, il che implica che al terminedell’algoritmo il <strong>preflow</strong> è un flusso. Poiché d(s) = n, la rete residua non contiene <strong>di</strong>rectpath dal nodo sorgente al nodo pozzo, il che, per il teorema 2.1, implica che il flusso èmassimo.Complessità dell’algoritmoPer analizzare la complessità iniziamo a stabilire un importante risultato: le <strong>di</strong>stance labelsono sempre valide ed il numero <strong>di</strong> incrementi subiti dalle <strong>di</strong>stance label è limitatosuperiormente. La prima <strong>di</strong> queste affermazioni segue dal lemma 2.7 poiché, comel’algoritmo shortest augmenting path, l’algoritmo <strong>preflow</strong>-<strong>push</strong> spinge flusso solo su archiammissibili e rietichetta un nodo solo quando non esistono archi ammissibili uscenti datale nodo. Dimostriamo ora la seconda affermazione.Lemma 3.1. Durante l’esecuzione dell’algoritmo <strong>preflow</strong>-<strong>push</strong>, ogni nodo i coneccesso positivo è connesso al nodo s da un <strong>di</strong>rect path dal nodo i al nodo s nella reteresidua.Dim. Notiamo che per un <strong>preflow</strong> X, si ha: e(s) ≤ 0 ed e(i) ≥ 0 per ogni i ∈ N \{s}.Per il teorema della decomposizione del flusso (ve<strong>di</strong> appen<strong>di</strong>ce A.1), possiamodecomporre ogni <strong>preflow</strong> X, relativamente alla rete originale G, in flussi non negativilungo (1) path dal nodo s al nodo t, (2) path dal nodo s ai no<strong>di</strong> attivi e (3) flussi su cicli<strong>di</strong>retti. Sia X un <strong>preflow</strong> in G e sia i un qualsiasi nodo attivo. La decomposizione del flusso<strong>di</strong> X deve contenere un <strong>di</strong>rect path P dal nodo s al nodo i, infatti i path dal nodo s al nodo te i flussi intorno ai cicli non contribuiscono all’eccesso del nodo i. La rete residua contieneil reverse <strong>di</strong> P, ovvero P in cui ogni arco è capovolto, e dunque un <strong>di</strong>rect path dal nodo i alnodo s.37


Tale lemma implica che durante un’operazione <strong>di</strong> relabel, l’algoritmo non minimizza su uninsieme vuoto.Lemma 3.2. Per ogni nodo i∈ N, d(i) < 2n.Dim. L’ultima volta che l’algoritmo ha rietichettato il nodo i, il nodo aveva uneccesso positivo, così la rete residua conteneva un <strong>di</strong>rect path P <strong>di</strong> lunghezza al più n-2 dalnodo i al nodo s. Il fatto che d(s) = n e che d(k) ≤ d(l)+1 per ogni arco (k, l) nel path Pimplica che d(i) ≤ d(s)+|P| < 2n.Poiché ogni volta che l’algoritmo rietichetta il nodo i, d(i) aumenta <strong>di</strong> almeno una unità,abbiamo stabilito il seguente risultato:Lemma 3.3. Ogni <strong>di</strong>stance label incrementa al più 2n volte. Di conseguenza, ilnumero totale <strong>di</strong> operazioni <strong>di</strong> relabel è al più 2n 2 .Lemma 3.4. L’algoritmo esegue al più nm <strong>push</strong>es saturating.Dim. Questo risultato deriva <strong>di</strong>rettamente dai lemmi 3.2 e 2.9.In base alla proprietà 2.8, il lemma 3.2 implica che il tempo necessario per identificarearchi ammissibili ed eseguire operazioni <strong>di</strong> relabel è O(nm). Valutiamo ora il numero <strong>di</strong><strong>push</strong> non saturating eseguiti dall’algoritmo.Lemma 3.5. L’algoritmo generic <strong>preflow</strong>-<strong>push</strong> esegue O(n 2 m) <strong>push</strong> non saturating.Dim. Proviamo il lemma usando le funzioni potenziali. Denotiamo con I l’insiemedei no<strong>di</strong> attivi. Consideriamo la funzione potenziale Φ =∑i∈ Id ( i). Poiché |I| < n e d(i)


<strong>di</strong> al più ε unità. Poiché, per il lemma 3.2, l’incremento totale <strong>di</strong> d(i) è limitato da 2n perogni nodo i, si ha che l’incremento totale della funzione Φ dovuto ad incrementi <strong>di</strong><strong>di</strong>stance label è limitato da 2n 2 .Secondo caso. L’algoritmo in<strong>di</strong>vidua un arco ammissibile su cui può inviare flusso, dunqueesegue un <strong>push</strong> saturating o un <strong>push</strong> non saturating. Un <strong>push</strong> saturating sull’arco (i, j) puòcreare un nuovo eccesso al nodo j, incrementando il numero dei no<strong>di</strong> attivi <strong>di</strong> uno eincrementando Φ <strong>di</strong> d(j). Essendo d(j) limitato da 2n ed il numero <strong>di</strong> <strong>push</strong> saturatinglimitato da nm, l’incremento totale dovuto ad operazioni <strong>di</strong> <strong>push</strong> saturating è limitato da2n 2 m. Notiamo ora che un <strong>push</strong> non saturating sull’arco (i, j) non incrementa |I|. Il <strong>push</strong>non saturating decrementerà Φ <strong>di</strong> d(i) poiché i <strong>di</strong>venterà non attivo. Se il nodo j <strong>di</strong>venteràattivo dopo il <strong>push</strong>, Φ incrementerà <strong>di</strong> d(j)=d(i)-1, dunque in totale Φ viene decrementata<strong>di</strong> 1. Se il nodo j era già attivo prima del <strong>push</strong>, Φ decresce <strong>di</strong> un valore pari a d(i). Diconseguenza Φ decresce <strong>di</strong> almeno una unità in corrispondenza <strong>di</strong> ogni <strong>push</strong> nonsaturating. Riepilogando, il valore iniziale <strong>di</strong> Φ è limitato da 2n 2 e il massimo incrementoche può subire è 2n 2 +2n 2 m. Ogni <strong>push</strong> non saturating decrementa Φ <strong>di</strong> almeno una unità.Di conseguenza, il numero <strong>di</strong> <strong>push</strong> non saturating è limitato da 2n 2 +2n 2 +2n 2 m = O(n 2 m).Specifichiamo ora come l’algoritmo tiene traccia dei no<strong>di</strong> attivi per le operazioni <strong>di</strong> <strong>push</strong> erelabel. L’algoritmo mantiene un insieme LIST <strong>di</strong> no<strong>di</strong> attivi, aggiunge a LIST i no<strong>di</strong> che<strong>di</strong>ventano attivi dopo una operazione <strong>di</strong> <strong>push</strong> e che non sono in LIST e cancella da LIST ino<strong>di</strong> che <strong>di</strong>ventano inattivi dopo un <strong>push</strong> non saturating. Diverse strutture dati (es. liste adoppio puntatore) possono essere utilizzate per la rappresentazione dell’insieme LIST inmodo tale che l’algoritmo possa aggiungere, cancellare o selezionare elementi in tempoO(1). Di conseguenza è facile implementare l’algoritmo <strong>preflow</strong>-<strong>push</strong> in modo tale che iltempo d’esecuzione sia O(n 2 m). Abbiamo così stabilito il seguente risultato:Teorema 3.6. L’algoritmo generic <strong>preflow</strong>-<strong>push</strong> impiega O(n 2 m) per determinareun flusso massimo.Implementazioni specifiche dell’algoritmo <strong>preflow</strong>-<strong>push</strong>Il tempo d’esecuzione dell’algoritmo precedente è comparabile con quello dell’algoritmoshortest augmenting path. Tuttavia, specificando regole <strong>di</strong>verse per la selezione dei no<strong>di</strong>39


attivi per le operazioni <strong>di</strong> <strong>push</strong> e relabel, otteniamo algoritmi <strong>di</strong>versi, ognuno dei quali concomplessità <strong>di</strong>versa da quella dell’algoritmo generico. Quello che è computazionalmenteoneroso nell’algoritmo generico <strong>preflow</strong> <strong>push</strong> è l’elevato numero <strong>di</strong> <strong>push</strong> non saturating,che può essere sostanzialmente ridotto attraverso specifiche regole per la selezione dei no<strong>di</strong>attivi.3.2 FIFO <strong>preflow</strong>-<strong>push</strong> algorithmDefiniamo innanzitutto il concetto <strong>di</strong> esame <strong>di</strong> un nodo. In una generica iterazione,l’algoritmo generic <strong>preflow</strong> <strong>push</strong>, una volta selezionato il nodo i, esegue un <strong>push</strong>,saturating o non saturating, oppure un’operazione <strong>di</strong> relabel. Se l’algoritmo esegue un <strong>push</strong>saturating, il nodo selezionato potrebbe essere ancora attivo, ma non è detto che esso vengaselezionato nuovamente nella successiva iterazione. Assumiamo che ogniqualvoltal’algoritmo <strong>preflow</strong> <strong>push</strong> seleziona un nodo attivo, esso esegue operazioni <strong>di</strong> <strong>push</strong> da talenodo finché l’eccesso del nodo <strong>di</strong>venta nullo o il nodo viene rietichettato. Di conseguenza,l’algoritmo potrebbe eseguire <strong>di</strong>versi <strong>push</strong> saturating seguiti da un <strong>push</strong> non saturating oun’operazione <strong>di</strong> relabel.L’algoritmo <strong>preflow</strong> <strong>push</strong> FIFO, esamina i no<strong>di</strong> nell’or<strong>di</strong>ne First In First Out, mantenendol’insieme LIST come una coda. Esso seleziona un nodo i dalla testa <strong>di</strong> LIST, esamina talenodo, ed aggiunge i no<strong>di</strong> <strong>di</strong>venuti attivi in coda a LIST. Se il nodo estratto <strong>di</strong>venta nonattivo, l’algoritmo estrae il nodo successivo in LIST. Se invece il nodo estratto vienerietichettato, l’algoritmo inserisce il nodo in coda a LIST. Illustriamo l’algoritmoconsiderando l’esempio in figura 3.3.(a)(b)Fig. 3.3. Funzionamento dell’algoritmo FIFO <strong>preflow</strong> <strong>push</strong>.L’operazione <strong>di</strong> preprocess crea un eccesso <strong>di</strong> 10 unità sui no<strong>di</strong> 2 e 3, la coda dei no<strong>di</strong>attivi è LIST={2, 3}. L’algoritmo estrae il nodo 2 dalla coda e lo esamina. Supponiamo40


che esso esegua un <strong>push</strong> saturating <strong>di</strong> 5 unità sull’arco (2, 4) e un <strong>push</strong> non saturating <strong>di</strong> 5unità sull’arco (2, 5), ottenendo la rete in figura 3.3 (b).(c)Fig. 3.3. Funzionamento dell’algoritmo FIFO <strong>preflow</strong> <strong>push</strong>.Dopo tali operazioni <strong>di</strong> <strong>push</strong> i no<strong>di</strong> 4 e 5 <strong>di</strong>ventano attivi e dunque saranno inseriti nellacoda, ottenendo LIST={3, 4, 5}. L’algoritmo estrae poi il nodo 3 dalla coda ed esegue un<strong>push</strong> saturating <strong>di</strong> 5 unità sull’arco (3, 5), seguito da un’operazione <strong>di</strong> relabel (figura 3.3(c)). L’algoritmo aggiunge il nodo 3 alla fine della coda, ottenendo LIST={4, 5, 3}.L’algoritmo procede in questo modo finché la coda non sarà vuota.Complessità dell’algoritmoPer analizzare la complessità nel caso peggiore, partizioniamo il numero totale <strong>di</strong> esami deino<strong>di</strong> in fasi <strong>di</strong>fferenti. La prima fase consiste nell’esame dei no<strong>di</strong> <strong>di</strong>venuti attivi a causadell’operazione preprocess. La seconda fase consiste nell’esame dei no<strong>di</strong> che sono attividopo che l’algoritmo ha esaminato tutti i no<strong>di</strong> nella prima fase. Allo stesso modo, la terzafase consiste nell’esame dei no<strong>di</strong> attivi che sono in coda dopo che l’algoritmo ha esaminatotutti i no<strong>di</strong> nella seconda fase, e così via. Nell’ esempio precedente, la prima fase consistenell’esame dei no<strong>di</strong> 2 e 3, la seconda fase consiste nell’esame dei no<strong>di</strong> 4, 5 e 3. Notiamoche l’algoritmo esamina un nodo al più una volta durante una fase.2n 2 +n.Lemma 3.6. Il numero <strong>di</strong> fasi eseguite dall’algoritmo è limitato superiormente daDim. Per limitare il numero <strong>di</strong> fasi eseguite dall’algoritmo, consideriamo la<strong>di</strong>fferenza tra i valori iniziale e finale della funzione potenziale Φ=max{ d(i): i è attivo} inun’intera fase. Consideriamo due casi.41


specificato come selezionare un nodo con la più grande <strong>di</strong>stance label in modo efficiente.Per tale selezione, utilizziamo la seguente struttura dati. Per ogni k = 1, 2,…, 2n-1l’algoritmo mantiene la lista LIST(k) = {i: i è attivo e d(i)=k}. Definiamo, inoltre, unavariabile level che rappresenta un limite superiore al più grande valore <strong>di</strong> k per cui LIST(k)è non vuota. Per determinare un nodo con la più grande <strong>di</strong>stance label, l’algoritmo esaminale liste LIST(level), LIST(level-1), …, finché non in<strong>di</strong>vidua una lista non vuota, <strong>di</strong>ciamoLIST(p). L’algoritmo pone allora level = p e seleziona un nodo in LIST(p). Inoltre, se la<strong>di</strong>stance label <strong>di</strong> un nodo cresce mentre l’algoritmo esamina tale nodo, l’algoritmo ponelevel pari alla nuova <strong>di</strong>stance label del nodo. Notiamo che, per il lemma 3.3, level puòcrescere <strong>di</strong> al più 2n 2 , per cui il decremento totale che può subire level è al più 2n 2 +n. Diconseguenza, la scansione delle liste LIST(level), LIST(level-1), …, fino ad in<strong>di</strong>viduare laprima lista non vuota, non è una operazione onerosa.Cerchiamo ora <strong>di</strong> capire perché il comportamento dell’algoritmo highest label non èpeggiore dell’algoritmo FIFO, considerando l’esempio dato in figura 3.4 (a). L’operazione<strong>di</strong> preprocess creerà un eccesso <strong>di</strong> un’unità in ognuno dei no<strong>di</strong> 2, 3,…, n-1 (ve<strong>di</strong> figura3.4(b)). L’algoritmo highest label, esaminerà nell’or<strong>di</strong>ne i no<strong>di</strong> 2, 3,…, n-1 e trasferiràtutto l’eccesso nel nodo pozzo. L’algoritmo FIFO potrebbe invece eseguire un numeromolto più grande <strong>di</strong> operazioni <strong>di</strong> <strong>push</strong>. Supponiamo che alla fine dell’operazione <strong>di</strong>preprocess, la coda dei no<strong>di</strong> attivi sia LIST={n-1, n-2,…,3,2}; l’algoritmo allora esamineràognuno <strong>di</strong> questi no<strong>di</strong> nella prima fase ottenendo la rete mostrata in figura 3.4(c).d(i)d(j)(a)8 8 8 8e(i)e(j)(b)8 8 8 8Fig. 3.4. Caso peggiore dell’algoritmo FIFO <strong>preflow</strong> <strong>push</strong>. (a): Rete residuainiziale. (b): Rete residua dopo l’operazione <strong>di</strong> preprocess.43


A questo punto, LIST={n-1, n-2,…, 4, 3}. Non è <strong>di</strong>fficile notare che l’algoritmo eseguiràn-2 fasi, nella prima delle quali eseguirà n-2 <strong>push</strong> non saturating, nella seconda ne eseguiràn-3 e così via per un totale <strong>di</strong> Ω(n 2 ) <strong>push</strong> non saturating.e(i)e(j)(c) 8 8 8 8Fig. 3.4. Caso peggiore dell’algoritmo FIFO <strong>preflow</strong> <strong>push</strong>. (c): Rete residua dopouna fase dell’algoritmo.Il precedente esempio illustra il vantaggio <strong>di</strong> spe<strong>di</strong>re flusso a partire dai no<strong>di</strong> con la piùgrande <strong>di</strong>stance label. L’algoritmo highest label inizia con lo spe<strong>di</strong>re flusso dai no<strong>di</strong> al piùalto livello, e spe<strong>di</strong>sce gli eccessi in questo livello verso i no<strong>di</strong> <strong>di</strong> livello successivo, edunque ripete tale processo. Così facendo, esso accumula gli eccessi e spe<strong>di</strong>sce tali eccessiaccumulati verso il pozzo. Di conseguenza, l’algoritmo highest label consente <strong>di</strong> ovviarealle ripetitive spe<strong>di</strong>zioni <strong>di</strong> quantità piccole <strong>di</strong> flusso lungo uno stesso arco. Talevantaggiosa caratteristica si traduce in un più stretto limite superiore al numero <strong>di</strong> <strong>push</strong> nonsaturating.Complessità dell’algoritmoPrima <strong>di</strong> mostrare che l’algoritmo esegue O(n 2 m 1/2 ) <strong>push</strong> non saturating, introduciamoalcune necessarie definizioni. In un qualsiasi istante durante l’esecuzione dell’algoritmo<strong>preflow</strong> <strong>push</strong>, ogni nodo <strong>di</strong>verso dal pozzo, ha al più un current arc che, per definizione,deve essere ammissibile. Denotiamo con F l’insieme dei current arc. L’insieme F ha al piùn-1 archi, al più un arco uscente per nodo, e non contiene alcun ciclo. F pertanto definisceuna foresta, a cui ci riferiremo in seguito come foresta corrente. La figura 3.5 mostra unesempio <strong>di</strong> foresta. Notiamo che ogni albero nella foresta è un albero ra<strong>di</strong>cato, la ra<strong>di</strong>ce èrappresentata dal nodo che non ha archi uscenti. Per ogni nodoi ∈ N denotiamo con D(i)l’insieme dei <strong>di</strong>scendenti <strong>di</strong> tale nodo nella foresta F. Per l’esempio in figura 3.5,D(1)={1}, D(2)={2}, D(3)={1, 3, 4}, D(4)={4}, D(5)={1, 2, 3, 4, 5}. Osserviamo che la<strong>di</strong>stance label <strong>di</strong> un <strong>di</strong>scendente <strong>di</strong> i è più grande <strong>di</strong> d(i). Definiamo nodo attivo massimale44


un nodo attivo che non ha <strong>di</strong>scendenti attivi. I no<strong>di</strong> attivi massimali nell’esempioconsiderato sono i no<strong>di</strong> 2, 4, e 8. Sia H l’insieme dei no<strong>di</strong> attivi massimali. Notiamo chedue no<strong>di</strong> attivi massimali non hanno <strong>di</strong>scendenti in comune. Osserviamo inoltre chel’algoritmo highest label spe<strong>di</strong>sce flusso a partire da un nodo attivo massimale.e(i)e(j)Fig. 3.5. Esempio <strong>di</strong> foresta corrente.Consideriamo ora la funzione potenziale Φ = ∑ ∈Φ( i) , dove Φ (i)è così definito:i HΦ ( i)= max{0, K + 1−| D(i)|} ; K è un parametro che specificheremo in seguito. Notiamoche per ogni i, Φ (i)è al più K in quanto | D ( i)| ≥ 1. Osserviamo inoltre che Φ cambiaogniqualvolta cambia l’insieme H dei no<strong>di</strong> attivi massimali o | D ( i)| cambia per un nodomassimale i. Stu<strong>di</strong>eremo ora l’effetto dell’esecuzione dell’algoritmo sulla funzionepotenziale Φ , al fine <strong>di</strong> ottenere un limite al numero <strong>di</strong> <strong>push</strong> non saturating. Primo,consideriamo un <strong>push</strong> non saturating su un arco (i, j) uscente da un nodo attivo massimalei. Un <strong>push</strong> non saturating avviene su un current arc e non cambia la foresta corrente, essosemplicemente sposta l’eccesso dal nodo i al nodo j (figura 3.6 (a)).Fig. 3.6 (a): Push non saturating sull’arco (3,4).Di conseguenza il nodo i <strong>di</strong>venta inattivo ed il nodo j potrebbe <strong>di</strong>ventare un nuovo nodoattivo massimale. Poiché | D ( j)| > | D(i)| , tale <strong>push</strong> decrementa la somma Φ ( i)+ Φ(j)<strong>di</strong>45


almeno un’unità seD( i)≤ K , altrimenti Φ ( i)+ Φ(j)resta invariata. Consideriamo ora un<strong>push</strong> saturating su un arco (i, j) uscente da un nodo massimale i (figura 3.6 (b)). In seguitoa tale <strong>push</strong>, l’arco (i, j) <strong>di</strong>venta inammissibile e non appartiene più alla foresta corrente.Fig. 3.6 (b): Push non saturating sull’arco (1,3).Il nodo i è ancora un nodo attivo massimale e il nodo j potrebbe esserlo <strong>di</strong>ventato. Diconseguenza, tale operazione potrebbe incrementare Φ <strong>di</strong> al più K unità. Esaminiamo oral’operazione <strong>di</strong> rietichettamento <strong>di</strong> un nodo attivo massimale i. Il rietichettamento avvienequando il nodo estratto non ha archi ammissibili, pertanto non esiste un current arc uscenteda questo nodo. Di conseguenza, il nodo i deve essere un nodo ra<strong>di</strong>ce nella forestacorrente. Ma poiché il nodo i è un nodo attivo massimale, nessuno dei suoi <strong>di</strong>scendenti puòessere attivo. Dopo che l’algoritmo ha rietichettato il nodo i, tutti gli archi entranti nel nodoi <strong>di</strong>ventano inammissibili. Pertanto tutti gli archi entranti nel nodo i non apparterranno piùalla foresta corrente (figura 3.6 (c)).Fig. 3.6 (c): Rietichettamento del nodo 5.Tale cambiamento non creerà alcun nuovo nodo massimale, tuttavia il numero <strong>di</strong> no<strong>di</strong><strong>di</strong>scendenti <strong>di</strong> i sarà decrementato ad 1. Di conseguenza, Φ (i)può incrementare <strong>di</strong> al piùK. Consideriamo infine l’introduzione <strong>di</strong> un nuovo current arc nella foresta corrente.L’aggiunta <strong>di</strong> nuovi current arc non crea alcun nuovo nodo attivo massimale, potrebbe anzi46


eliminare qualche nodo attivo massimale incrementando il numero <strong>di</strong> <strong>di</strong>scendenti <strong>di</strong> undato nodo (figura 3.6 (d)).Fig. 3.6 (d):Aggiunta dell’arco (3,5) alla foresta.In ogni caso la funzione potenziale Φ non incrementa. Abbiamo dunque stabilito laseguente:Proprietà 3.8.a) Un <strong>push</strong> non saturating da un nodo attivo massimale i non incrementa Φ ; essodecrementa Φ <strong>di</strong> almeno un’unità se D( i)≤ K .b) Un <strong>push</strong> saturating da un nodo attivo massimale i può incrementare Φ <strong>di</strong> alpiù K unità.c) Il rietichettamento <strong>di</strong> un nodo attivo massimale i può incrementare Φ <strong>di</strong> al piùK unità.d) L’introduzione <strong>di</strong> current arc non incrementa Φ .Definiamo ora il concetto <strong>di</strong> fase. Una fase è costituita dalla sequenza <strong>di</strong> <strong>push</strong> tra dueconsecutive operazioni <strong>di</strong> relabel. Il lemma 3.3 implica che l’algoritmo eseguirà O(n 2 ) fasi.Diremo che una fase è economica se il numero <strong>di</strong> <strong>push</strong> non saturating in essa eseguiti èlimitato superiormente da 2n/K, altrimenti <strong>di</strong>remo cha la fase è espansiva. Ovviamente ilnumero <strong>di</strong> <strong>push</strong> non saturating in fasi economiche è al più O(n 2 • 2n/K)=O(n 3 /K). Perdefinizione, in una fase espansiva vengono eseguiti almeno 2n/K <strong>push</strong> non saturating.Poiché la rete può contenere al più n/K no<strong>di</strong> con K <strong>di</strong>scendenti o più, almeno n/K <strong>push</strong> nonsaturating devono verificarsi tra no<strong>di</strong> con meno <strong>di</strong> K <strong>di</strong>scendenti. L’algoritmo highest label<strong>preflow</strong> <strong>push</strong> effettua operazioni <strong>di</strong> <strong>push</strong> e relabel su no<strong>di</strong> attivi massimali, possiamopertanto applicare la proprietà 3.8, la quale implica che ognuno <strong>di</strong> tali <strong>push</strong> non saturating47


deve decrementare Φ <strong>di</strong> almeno un’unità. Le parti (b) e (c) della precedente proprietàimplicano che il totale incremento in Φ dovuto ad operazioni <strong>di</strong> <strong>push</strong> saturating e relabel èal più O(nmK). Di conseguenza, l’algoritmo può eseguire O(nmK) <strong>push</strong> non saturating inua fase espansiva.Ricapitolando la precedente <strong>di</strong>scussione, notiamo che nella fase economica vengonoeseguiti O(n 3 /K) <strong>push</strong> non saturating e nella fase espansiva O(nmK). Otteniamo il valoreottimo <strong>di</strong> K bilanciando (ve<strong>di</strong> appen<strong>di</strong>ce A.2) entrambi i termini, ovvero quando i terminisono uguali: n 3 /K =nmK, da cui K= n/m 1/2 . Per tale valore <strong>di</strong> K il numero <strong>di</strong> <strong>push</strong> nonsaturating è O(n 2 m 1/2 ). Abbiamo pertanto stabilito il seguente:Teorema 3.9. L’algoritmo highest label <strong>preflow</strong> <strong>push</strong> esegue O(n 2 m 1/2 ) <strong>push</strong> nonsaturating ed il suo tempo d’esecuzione è O(n 2 m 1/2 ).48


Capitolo 4Algoritmo AuctionPresentiamo ora un algoritmo che può essere considerato un approccio ibrido tra i meto<strong>di</strong><strong>di</strong>scussi precedentemente, in quanto sfrutta concetti sia degli algoritmi augmenting path,sia degli algoritmi <strong>preflow</strong>-<strong>push</strong>. Esso in<strong>di</strong>vidua augmenting path nella rete residua <strong>di</strong>lunghezza non necessariamente minima. L’idea alla base <strong>di</strong> tale metodo è che l’invio <strong>di</strong>flusso verso il pozzo necessita semplicemente <strong>di</strong> un <strong>di</strong>rect path da s a t nella rete residua el’insistenza nell’in<strong>di</strong>viduazione <strong>di</strong> un <strong>di</strong>rect path <strong>di</strong> lunghezza minima può implicare unconsiderevole costo computazionale. Dopo aver descritto tale metodo, estenderemo lalogica alla base <strong>di</strong> tale algoritmo, proponendo un algoritmo dagli interessanti risultatipratici.4.1 Path construction algorithmDescriviamo ora un metodo per in<strong>di</strong>viduare un <strong>di</strong>rect path tra due no<strong>di</strong> <strong>di</strong> un grafo G=(N,A). Tale metodo è alla base dell’algoritmo per il massimo flusso che presenteremo nelparagrafo 4.3.Ricor<strong>di</strong>amo, dalle definizioni date nel capitolo primo, che un <strong>di</strong>rect walk P è una sequenza<strong>di</strong> no<strong>di</strong> (i 1 , i 2 , …, i r ) con r ≥ 2, ed una corrispondente sequenza <strong>di</strong> r-1 archi tali che l’i-moarco nella sequenza è l’arco (i i, i i+1 ); i 1 è il nodo iniziale del walk, i r è il nodo terminale.L’algoritmo che presentiamo consente <strong>di</strong> in<strong>di</strong>viduare un <strong>di</strong>rect path, ovvero un <strong>di</strong>rect walksenza alcuna ripetizione <strong>di</strong> no<strong>di</strong>, che inizia ad un dato nodo i 1 ed ha come nodo terminaleun nodo t. Esso mantiene un <strong>di</strong>rect walk P=(i 1 , i 2 , …, i r ) ed un vettore <strong>di</strong> interi p dove p(i)rappresenta il “prezzo” associato al nodo i. Il <strong>di</strong>rect walk P ed il vettore p sod<strong>di</strong>sfano leseguenti con<strong>di</strong>zioni:1. p( i)≤ p(j)+ 1 ∀ ( i,j)∈A, (4.1)49


2. p(i 1 ) < n, p (t)= 0, (4.2)3. p( i)≥ p(j)∀ ( i,j)∈P. (4.3)L’algoritmo è motivato dal contesto del massimo flusso, dove l’obiettivo non è in<strong>di</strong>viduareun singolo path, ma una sequenza <strong>di</strong> path ognuno dei quali in un grafo che <strong>di</strong>fferisceminimamente dal suo predecessore; infatti la rete residua dopo un aumento su undeterminato augmenting path, <strong>di</strong>fferirà solo leggermente dalla stessa rete residua primadell’operazione <strong>di</strong> aumento. In tale contesto, i prezzi associati ai no<strong>di</strong> risultano utili nelguidare la ricerca per nuovi path. Il vettore dei prezzi è infatti mo<strong>di</strong>ficato dall’algoritmo inmodo tale che i path desiderati sono <strong>di</strong>retti approssimativamente verso il basso, nel sensoche essi procedono da no<strong>di</strong> con prezzo maggiore verso no<strong>di</strong> con prezzo minore. Pertanto,se un determinato insieme <strong>di</strong> prezzi è appropriato per guidare la ricerca in un dato grafo,esso sarà approssimativamente appropriato per guidare la ricerca <strong>di</strong> un path in un grafoleggermente <strong>di</strong>fferente. All’inizio dell’algoritmo, il <strong>di</strong>rect walk P è costituito (con un abuso<strong>di</strong> notazione) dal solo nodo i 1 , ed il vettore p è tale da sod<strong>di</strong>sfare le con<strong>di</strong>zioni (4.1) e (4.2)sopraelencate (una possibile inizializzazione per p è p ( i)= 0 ∀ i ∈ N ). Il <strong>di</strong>rect walk P èmo<strong>di</strong>ficato utilizzando le seguenti due operazioni:a) Una contrazione <strong>di</strong> P, che elimina l’ultimo arco <strong>di</strong> P, ovvero sostituisce il walkP=(i 1 , i 2 , …, i t-1 , i t ) con il walk P=(i 1 , i 2 , …, i t-1 ). Nel caso particolare in cui P=(i 1 ),l’operazione <strong>di</strong> contrazione non mo<strong>di</strong>fica P.b) Una estensione <strong>di</strong> P, che aggiunge al walk P un arco uscente dal suo nodoterminale, ovvero sostituisce il walk P=(i 1 , i 2 , …, i t ) con un walk P=(i 1 , i 2 , …, i t ,i t+1 ), dove (i t , i t+1 ) ∈ A(i t ).50


algorithm path constructionbeginP=(i 1 );i t =i 1 ;Seleziona un vettore p che sod<strong>di</strong>sfi le con<strong>di</strong>zioni (4.1) e (4.2);while(TRUE)beginif N(i t ) = ∅ thenp(i t ):=n;Contract(P);elsebeginsucc(i t ):= argminp(j) ;j∈N(it)p(i t ):=p(succ(i t )) + 1;if i t = i 1 OR p(pred(i t )) > p(succ(i t )) then Extend(P, succ(i t ));else Contract(P);end;end;end;procedure Extend(P, j)beginSostituisci P=(i 1 , i 2 , …, i t ) con P=(i 1 , i 2 , …, i t , j);if j=t then terminate;end;procedure Contract(P,j)begin51


if P=(i 1 ) AND p(i 1 ) ≥ n then terminate;Sostituisci P=(i 1 , i 2 , …, i t-1 , i t ) con P=(i 1 , i 2 , …, i t-1 );end;Diamo ora un esempio del funzionamento dell’algoritmo, per poi passare ad evidenziarnealcune fondamentali proprietà.(a)(b)Fig. 4.1. Esempio <strong>di</strong> funzionamento dell’algoritmo path construction.(a): Rete iniziale. (b): Rete al termine dell’algoritmo.All’ inizio dell’Walk Piterazione #Vettore dei prezzi p Operazione effettuata1 P=(1) (0,0,0,0,0,0,0) Estensione su 2.2 P=(1,2) (1,0,0,0,0,0,0) Estensione su 3.3 P=(1,2,3) (1,1,0,0,0,0,0) Estensione su 5.4 P=(1,2,3,5) (1,1,1,0,0,0,0) Contrazione, N(5)= ∅ .5 P=(1,2,3) (1,1,1,0,7,0,0)Contrazione,p(pred(3))


All’ inizio dell’Walk Piterazione #Vettore dei prezzi p Operazione effettuata9 P=(1) (1,9,8,8,7,0,0) Estensione su 6.10 P=(1,6) (1,9,8,8,7,0,0)Estensione su 7. n t =7,Stop.Proprietà 4.1. I prezzi restano interi durante l’esecuzione dell’algoritmo.Ovvio, in quanto in fase <strong>di</strong> inizializzazione, il vettore p è scelto come vettore <strong>di</strong>interi e l’unica operazione che mo<strong>di</strong>fica i prezzi è l’aggiornamento del prezzo <strong>di</strong> p(i t ) nelcorpo del ciclo while dell’algoritmo, ovvero p(i t )=p(succ(i t )) + 1.Proprietà 4.2. Le con<strong>di</strong>zioni (4.1), (4.2) e (4.3) sono sod<strong>di</strong>sfatte ogniqualvoltal’algoritmo entra nel ciclo while.La con<strong>di</strong>zione (4.2) è banalmente mantenuta vera durante l’esecuzionedell’algoritmo, in quanto l’algoritmo termina quando la con<strong>di</strong>zione (4.2) non è più vera,ovvero quando p(i 1 ) ≥ n.Dimostriamo la proposizione per induzione sul numero <strong>di</strong> volte in cui l’algoritmo entra nelciclo while. Le con<strong>di</strong>zioni (4.1) e (4.3) valgono, per base d’induzione, all’iniziodell’algoritmo. Consideriamo una generica iterazione dell’algoritmo, e supponiamo che nelmomento in cui l’algoritmo entra nel ciclo while, le con<strong>di</strong>zioni (4.1) e (4.3) valgono.Dimostriamo che tali con<strong>di</strong>zioni valgono all’inizio della successiva iterazione. Notiamoche nel corpo del ciclo while, solo il prezzo <strong>di</strong> i t può cambiare. Ed in particolarel’algoritmo pone p(i t )=p(succ(i t )) + 1; ma succ(i t )= arg min p(j), il che implicap(succ(i t )) ≤ p( j)∀ j∈N ( it)e dunque p(i t )=p(succ(i t )) + 1 ≤ p( j)+ 1 ∀ j∈N( it)pertantoj ∈N(it)53


la con<strong>di</strong>zione (4.1) viene sod<strong>di</strong>sfatta per ogni arco, compreso l’arco (i t, succ(i t )).Consideriamo ora la con<strong>di</strong>zione (4.3): poiché p(i t ) viene mo<strong>di</strong>ficato, l’arco (pred(i t ) , i t )potrebbe violare tale con<strong>di</strong>zione. Notiamo però che successivamente alla mo<strong>di</strong>fica <strong>di</strong> p(i t )si ha una contrazione o una estensione. Nel caso della contrazione, i t viene eliminato dalwalk, quin<strong>di</strong> l’arco (pred(i t ) , i t ) ∉ P . L’estensione avviene invece quando p(pred(i t )) >p(succ(i t )), essendo i prezzi interi, la precedente <strong>di</strong>suguaglianza è equivalente ap(pred(i t )) ≥ p(succ(i t ))+1, ma p(succ(i t ))+1 = p(i t ), pertanto la con<strong>di</strong>zione (4.3) èsod<strong>di</strong>sfatta per ogni arco del path P.Proprietà 4.3. Una contrazione determina un aumento <strong>di</strong> prezzo.Sia i t il nodo terminale del <strong>di</strong>rect walk P e supponiamo che l’algoritmo stia perentrare nel corpo del ciclo while. Per la proposizione precedente, prima <strong>di</strong> entrare nel ciclo,vale la con<strong>di</strong>zione (4.3), per cui p(pred(i t )) ≥ p(i t ). Affinché vi sia una contrazione nel ciclowhile, p(pred(i t )) ≤ p(succ(i t )), da cui p(i t ) ≤ p(succ(i t )). Ma l’algoritmo precedentemente haposto p(i t )=p(succ(i t )) + 1, incrementando il valore <strong>di</strong> p(i t ).Non è detto invece che un’estensione sia accompagnata sempre da un incremento <strong>di</strong>prezzo, come è possibile notare dall’iterazione numero 9 dell’esempio precedente.Archi uphill e downhill. Dati un vettore p <strong>di</strong> prezzi interi, <strong>di</strong>remo che un arco ( i , j)èuphill se p (i) < p(j), downhill se p( i)≥ p(j)e strettamente downhill se p ( i)= p(j)+ 1.Proprietà 4.4. Il <strong>di</strong>rect walk P è un <strong>di</strong>rect path , ed in seguito ad un’estensione,l’ultimo arco <strong>di</strong> P è strettamente downhill.Sia P=(i 1 , i 2 , …, pred(i t ), i t ). Dalla con<strong>di</strong>zione (4.3) si ha chep( i)≥ p(j)∀ ( i , j)∈Ρ. Supponiamo, per assurdo, che l’algoritmo si estenda su un nodo54


succ(i t ) appartenente a P. L’estensione avviene quando p(pred(i t ))>p((succ(i t )), ma poichésucc(i t )∈Psi ha: p(pred(i t ))>p((succ(i t )) ≥ p(pred(i t )), assurdo.Ner ritornare nel corpo del while in seguito ad un’estensione, l’algoritmo ha postop(pred(i t ))=p(i t )+1, dunque l’ultimo arco <strong>di</strong> P, in seguito ad un’estensione, è strettamentedownhill.Dalle proposizioni precedenti deriva il seguenteTeorema 4.5. Durante l’esecuzione dell’algoritmo, il vettore dei prezzi sod<strong>di</strong>sfa lecon<strong>di</strong>zioni (4.1) e (4.2), il <strong>di</strong>rect walk P è un <strong>di</strong>rect path, i suoi archi sono downhill(con<strong>di</strong>zione (4.2)), ed in seguito ad un’estensione, l’ultimo arco <strong>di</strong> P è strettamentedownhill.Dimostriamo ora il seguenteTeorema 4.6. Se esiste un <strong>di</strong>rect path da i 1 a t, l’algoritmo termina in seguito adun’estensione con tale path. Altrimenti, l’algoritmo termina in seguito ad una contrazione.Dim. Dimostriamo innanzitutto che l’algoritmo termina. Notiamo che, durantel’esecuzione dell’algoritmo, i prezzi dei no<strong>di</strong> in P sono strettamente minori <strong>di</strong> n. Infatti, seesistesse un nodo nel path P con un prezzo almeno n, la con<strong>di</strong>zione (4.3) che vale durantel’esecuzione dell’algoritmo, implicherebbe p(i 1 ) ≥ n, che causerebbe la terminazionedell’algoritmo. Inoltre in corrispondenza <strong>di</strong> ogni contrazione si ha un incremento <strong>di</strong> prezzo<strong>di</strong> almeno un’unità, ed essendo i prezzi dei no<strong>di</strong> in P limitati da n, il numero <strong>di</strong> contrazioniè finito. Osserviamo che tra una contrazione e quella successiva possono esistere al più n-1estensioni consecutive. Infatti se ne esistessero più <strong>di</strong> n-1, significherebbe che l’algoritmosi è esteso su un nodo che già appartiene a P, ma per quanto <strong>di</strong>mostrato precedentementequesto non è possibile. Dunque essendo il numero <strong>di</strong> contrazioni limitato, e tra unacontrazione e l’altra può esserci un numero limitato <strong>di</strong> estensioni, l’algoritmo termina.55


Dimostriamo ora che se l’algoritmo termina in seguito ad una contrazione, non esiste unpath da i 1 a t. Dalla proprietà 4.2, si ha che durante l’esecuzione dell’algoritmo, p(t)=0 ep ( i)≤ p(j)+ 1 ∀ ( i , j)∈Α . Dunque se esiste un <strong>di</strong>rect path da i 1 a t, p(i 1 ) < n. Infatti un<strong>di</strong>rect path da i 1 a t può contenere al più n - 1 archi e poiché per ogni arco vale chep ( i)≤ p(j)+ 1, p(i 1 ) può essere al più n - 1. Pertanto, se l’algoritmo termina in seguito aduna contrazione, il che avviene quando p(i 1 ) ≥ n, non esiste un <strong>di</strong>rect path da i 1 a t.4.2 Miglioramento dell’algoritmo path constructionNel precedente algoritmo buona parte dell’elaborazione è necessaria, dato il nodoterminale i t <strong>di</strong> un path P, ad in<strong>di</strong>viduare il nodo succ(i t ) in corrispondenza del quale si ha ilminimo peso tra i no<strong>di</strong> a<strong>di</strong>acenti ad i t . Tuttavia, è possibile che per un determinato nodoesistano più no<strong>di</strong> a<strong>di</strong>acenti con uno stesso valore minimo. Ha senso, pertanto, memorizzaretali no<strong>di</strong> (ad i relativi archi) in una struttura dati e cercare <strong>di</strong> riutilizzarla per quantopossibile senza mo<strong>di</strong>ficare le essenziali proprietà dell’algoritmo, ovvero il sod<strong>di</strong>sfacimentodelle con<strong>di</strong>zioni (4.1), (4.2), e (4.3), e la garanzia che il walk P sia costituito da no<strong>di</strong><strong>di</strong>stinti. Per sfruttare tale idea, l’algoritmo mantiene per ogni nodo i ≠ t, un sottoinsiemedegli archi uscenti da i denotato Cand(i) che chiameremo l’insieme can<strong>di</strong>dato degli archidel nodo i. L’insieme dei no<strong>di</strong> terminali degli archi in Cand(i), sarà denotato con Succ(i).L’insieme Cand(i) ed il vettore dei prezzi p(i) definiscono un grafo, che chiameremo ilgrafo ammissibile, il cui insieme dei no<strong>di</strong> è N, ovvero lo stesso insieme dei no<strong>di</strong> del grafo<strong>di</strong> partenza, e l’insieme degli archi è A = {(i, j) | j ∈succ(i), p(i) ≥ p(j), i∈ N }.Durante l’esecuzione dell’algoritmo al variare degli insiemi Succ(i) e dei prezzi p(i) varia ilgrafo ammissibile che tuttavia resta aciclico, come <strong>di</strong>mostreremo in seguito, fino al terminedell’ algoritmo. Inizialmente l’insieme Cand(i) e il vettore dei prezzi p(i) sono tali che ilgrafo ammissibile è aciclico. Tale con<strong>di</strong>zione è banalmente sod<strong>di</strong>sfatta se Cand(i)=∅ ∀i∈ N e p ( i)= 0 ∀ i ∈ N .56


algorithm mo<strong>di</strong>fied path constructionbeginP:=(i 1 );i t :=i 1 ;Seleziona un vettore p che sod<strong>di</strong>sfi le con<strong>di</strong>zioni (4.1) e (4.2);while(TRUE)beginif esiste un nodo l∈ Succ(i t ) tale che p(i t ) ≥ p(l) then Extend(P,l);elsif N(i t ) = ∅ thenp(i t ):=n;Contract(P);elsebeginSucc(i t ):={ l |p(l) = minp(j) };j∈N(it)Cand(i t ):={(i t , l) ∈ A(i t ) | l∈ Succ(i t )};Seleziona un nodo l∈ Succ(i t );p(i t ):=p(l) + 1;if i t = i 1 OR p(pred(i t )) > p(l) then Extend(P,l);else Contract(P);end;end;end;Consideriamo ora un esempio <strong>di</strong> funzionamento dell’algoritmo,in cui il vettore p è statoscelto come p=(0, 0, 0, 0, 1, 0), al fine <strong>di</strong> evidenziare l’utilità degli insiemi ausiliariSucc(i) e Cand(i).57


(a)(b)Fig. 4.2 Esempio <strong>di</strong> funzionamento dell’algoritmo path construction migliorato.(a): Situazione iniziale. (b): Al termine dell’algoritmo.# Path PVettore deiprezzi p(Succ(1),Succ(2),…, Succ(6)) Operazioni effettuate1 P=(1) (0,0,0,0,1,0) (∅ ,∅ ,∅ ,∅ ,∅ ,∅ )Costruzione Succ(1),estensione su 2.2 P=(1,2) (1,0,0,0,1,0) ({2,3},∅ ,∅ ,∅ ,∅ ,∅ )Costruzione Succ(2),estensione su 4.3 P=(1,2,4) (1,1,0,0,1,0) ({2,3},{4},∅ ,∅ ,∅ ,∅ ) Contrazione, N(4)= ∅ .4 P=(1,2) (1,1,0,6,1,0) ({2,3},{4},∅ ,∅ ,∅ ,∅ )Ricostruzione Succ(2),contrazione,p(pred(2))


La maggior efficienza <strong>di</strong> tale versione relativamente alla precedente è dovuta almantenimento degli insiemi Succ(i t ) e Cand(i t ). Si consideri l’iterazione 5 del precedenteesempio. Come è possibile notare, l’algoritmo estende il path P=(1) sul nodo 3. Conun’opportuna implementazione, l’in<strong>di</strong>viduazione del nodo su cui deve essere esteso il pathè effettuata in tempo costante; mentre nella versione precedente sarebbe stato necessarioanalizzare nuovamente i vicini del nodo 1 per in<strong>di</strong>viduare il nodo con prezzo minimo. Inmodo simile alla prima versione dell’algoritmo, ogni contrazione è accompagnata da unincremento del prezzo p(i t ). Ed ancora analogamente, un’estensione può essereaccompagnata o meno da un incremento <strong>di</strong> prezzo. Le due versioni dell’algoritmo<strong>di</strong>fferiscono invece nel test effettuato al fine <strong>di</strong> effettuare un’operazione <strong>di</strong> estensione. Inparticolare, nella seconda versione <strong>di</strong> tale algoritmo, viene effettuata un’estensione su unnodo l∈Succ(i t ) anche quando p(i t ) = p(l), causando che l’ultimo arco <strong>di</strong> P non siastrettamente downhill in seguito ad un’estensione, cosi come avviene nella prima versione.Per tale motivo non è ovvio che un’estensione non creerà un ciclo nel walk P. Per<strong>di</strong>mostrare che il <strong>di</strong>rect walk P è <strong>di</strong>rect path , <strong>di</strong>mostriamo le seguenti proposizioni.Proprietà 4.7. Gli archi <strong>di</strong> P appartengono al grafo ammissibile.E’ necessario <strong>di</strong>mostrare che ∀ ( i , j)∈Ρ: j∈Succ(i) e p( i)≥ p(j). Dimostriamo talerisultato per induzione sul numero <strong>di</strong> operazioni <strong>di</strong> estensione e contrazione che possonoessere eseguite su P. In particolare <strong>di</strong>mostriamo che se P appartiene al grafo ammissibile eviene eseguita un’operazione <strong>di</strong> contrazione o estensione, il walk risultante appartieneancora al grafo ammissibile.Se l’operazione subita da P è una contrazione, questa elimina solo l’arco terminale <strong>di</strong> P,lasciando inalterati i prezzi dei no<strong>di</strong> terminali degli altri archi in P. Pertanto se P=(i 1 , i 2 , …,i t ) è il walk prima della contrazione e∀ ( i , j)∈Ρ: j∈Succ(i) e p( i)≥ p(j), tale con<strong>di</strong>zionevale anche per il walk P’=(i 1 , i 2 , …, i t-1 , i t ) dopo la contrazione. Consideriamo oral’operazione <strong>di</strong> estensione. Tale operazione aggiunge al path P un arco <strong>di</strong> Cand(i t ), e siaquando il test p(i t ) ≥ p(l) è sod<strong>di</strong>sfatto, sia quando p(i t ) è settato a p(l) + 1, l’arco (i t , l) èdownhill, ovvero p(i t ) ≥ p(l).59


aciclico.Proprietà 4.8. Durante l’esecuzione dell’algoritmo, il grafo ammissibile restaNella fase d’inizializzazione Cand(i) e p(i) sono scelti in modo tale che il grafoammissibile sia aciclico. Dunque la base d’induzione vale per costruzione. Consideriamouna generica iterazione e ipotizziamo che il grafo ammissibile sia aciclico all’inizio delcorpo del ciclo while. Dimostriamo che il grafo è aciclico all’inizio della successivaiterazione. Distinguiamo due casi in base al fatto che esista o meno un nodo l∈ Succ(i t )sod<strong>di</strong>sfacente il test p(i t ) ≥ p(l). Se tale nodo l esiste, il grafo ammissibile non è mo<strong>di</strong>ficatonel ciclo while. Nel secondo caso, viene nuovamente calcolato l’insieme Cand(i t ), che puòora contenere archi che precedentemente non appartenevano al grafo ammissibile,causando eventualmente un ciclo. Dopo aver calcolato Cand(i t ), l’algoritmo ponep(i t )=p(l) + 1, e quin<strong>di</strong> tutti gli archi in Cand(i t ) sono strettamente downhill, ovverop(i t )=p(l) + 1 ∀ l∈ Succ(i t ). Ma allora tali archi non possono essere parte <strong>di</strong> un ciclo nelgrafo ammissibile, in cui, per definizione, tutti gli archi sono downhill. Ovverosupponiamo per assurdo che esista un arco strettamente downhill (i t , l) in Cand(i t ) cherende il grafo ammissibile ciclico. Affinché tale arco chiuda il ciclo, esiste nel grafoammissibile un percorso da l ad i t . Ma il grafo ammissibile è costituito da archi downhill,ovvero p(l) ≥ p(i t )=p(l) + 1> p(l), assurdo.Dunque gli archi <strong>di</strong> P appartengono al grafo ammissibile, che durante l’esecuzionedell’algoritmo è aciclico, pertanto P è un <strong>di</strong>rect path. Riassumiamo tali proprietà nelseguenteTeorema 4.9. Assumiamo che inizialmente il grafo ammissibile sia aciclico. Allora:a) Durante l’esecuzione dell’algoritmo, il grafo ammissibile resta aciclico.b) Il vettore dei prezzi sod<strong>di</strong>sfa le con<strong>di</strong>zioni (4.1) e (4.2), il walk P è un <strong>di</strong>rect walk, egli archi <strong>di</strong> P sono downhill.c) Se esiste un <strong>di</strong>rect path da i 1 a t, l’algoritmo termina in seguito ad un’estensionecon tale path. Altrimenti, l’algoritmo termina in seguito ad una contrazione.60


Dim. La parte (a) deriva dalla proprietà 4.8, (b) e (c) derivano dalla proprietà 4.7 e daiteoremi 4.5 e 4.6.4.3 Algoritmo principalePrima <strong>di</strong> passare alla descrizione dell’algoritmo, introduciamo alcune definizioni.Ricor<strong>di</strong>amo, dalle definizioni date nel capitolo 1, che data una rete capacitata G=(N, A) edun flusso x (o un <strong>preflow</strong> x) la rete residua indotta da x è il grafo G(x)=(N, A’) doveA’={(i, j) ∈ NxN : r ij >0}. Dato un <strong>preflow</strong> x, definiamo per ogni nodo i, l’insieme degliarchi eleggibili <strong>di</strong> i come A( i,x)= {( i,j)| ( i,j)∈ A'}, dove A’ è l’insieme degli archi dellarete residua indotta da x. Noto A ( i,x), definiamo l’insieme dei vicini eleggibili <strong>di</strong> i comeN( i,x)= {( i,j)| ( i,j)∈ A(i,x)}.L’algoritmo che presentiamo in tale paragrafo termina con un <strong>preflow</strong> x ed un taglio saturo[ S , S]tali che gli eccessi e(i) sod<strong>di</strong>sfano le con<strong>di</strong>zioni:e ( s)≤ 0 , e( i)≥ 0 ∀ i ≠ s , e( i)= 0 ∀ i ∈ S,i ≠ t . (4.4)Successivamente <strong>di</strong>mostreremo che tale taglio è un taglio minino e mostreremo come essopuò essere utilizzato insieme ad x per costruire un massimo flusso. Diremo che un <strong>preflow</strong>x ed un vettore dei prezzi p= { p(i)| i ∈ N}costituiscono una coppia valida sep( i)≤ p(j)+ 1 ∀ j ∈ N(i,x). (4.5)L’algoritmo inizia con una coppia valida (x, p) tale chee ( s)≤ 0 , e( i)≥ 0 ∀ i ≠ s , (4.6)p ( s)= n , p ( t)= 0 , p ( i)≥ 0 ∀ i ≠ s,t . (4.7)Una possibile scelta iniziale è costituita dal <strong>preflow</strong> x dato da:x ij = uij se i = s, ij = 0x se i ≠ s (4.8a)e dal vettore dei prezzi p dato da:p ( i)= n se i = s, p (i) = “lunghezza <strong>di</strong> uno shortest <strong>di</strong>rect path da i a t” se i ≠ s. (4.8b)61


Notiamo che la lunghezza <strong>di</strong> uno shortest <strong>di</strong>rect path da i a t può essere ottenutaeffettuando una visita in ampiezza nella rete residua a partire dal nodo pozzo. L’algoritmomantiene una coppia valida (x, p) sod<strong>di</strong>sfacente le con<strong>di</strong>zioni 4.5, 4.6 e 4.7, esegue unasequenza <strong>di</strong> iterazioni, e termina con un taglio minimo. All’inizio <strong>di</strong> ogni iterazione vieneselezionato un nodo i1 ≠ t tale che p ( i1 ) < n e e ( i1 ) > 0 . L’algoritmo cerca dunque <strong>di</strong>costruire un augmenting path a partire da i 1 utilizzando l’algoritmo path constructiondescritto precedentemente applicato alla rete residua, utilizzando il vettore <strong>di</strong> prezzi p. Sel’algoritmo riesce ad in<strong>di</strong>viduare un augmenting path, esso aumenta il flusso lungo talepath. Se non è possibile in<strong>di</strong>viduare un augmenting path a partire da i 1 , l’algoritmo pathconstruction terminerà con p( i1 ) ≥ n , in modo tale che i 1 non sarà scelto in iterazionisuccessive. Analogamente all’algoritmo path construction, l’algoritmo mantiene per ogninodo i, un insieme <strong>di</strong> archi incidenti in i, denotato con Cand(i). Tale insieme è vuoto peri = s , i = t, e per tutti i no<strong>di</strong> i con p ( i)= n . Denotiamo con Succ(i) l’insieme dei no<strong>di</strong> <strong>di</strong>Cand(i) opposti ad i. L’algoritmo richiede che inizialmente p ( i)= p(j)+ 1 se j ∈ Succ(i),il che sarà vero se tutti gli insiemi Cand(i) sono vuoti, oppure se per ogni nodo i abbiamoCand(i)= {( i,j)∈ A(i,x)| p(i)= p(j)+ 1} U {( j,i)∈ A(i,x)| p(i)= p(j)+ 1}, (4.9)dove (x, p) è la coppia valida iniziale. Se utilizziamo l’inizializzazione specificata nelle(4.8a) e (4.8b), allora Cand(i) come specificato dall’equazione (4.9), è l’insieme degli archiuscenti da i in uno shortest augmenting path a partire da i. Specifichiamo formalmentel’algoritmo.procedure Extend(P, j)beginsostituisci P=(i 1 , i 2 , …, i t ) con P=(i 1 , i 2 , …, i t , j);if j=t then Augment;end;procedure Contract(P, j)beginif P=(i 1 ) AND p(i 1 ) ≥ n then termina l’iterazione del ciclo while;sostituisci P=(i 1 , i 2 , …, i t-1, i t ) con P=(i 1 , i 2 , …, i t-1 );end;62


algorithm Auction/Max-Flowbeginseleziona una coppia (x, p) che sod<strong>di</strong>sfi le con<strong>di</strong>zioni (4.6) e (4.7);while esiste un nodo i 1beginseleziona un nodo i 1P:=(i 1 );i t := i 1 ;while (TRUE)begin≠ t tale che e(i 1 ) > 0 e p(i 1 ) < n do≠ t tale che e(i 1 ) > 0 e p(i 1 ) < n;if esiste j ∈ Succ(it)I N(it ,x)tale che (i ) p(j)Extend(P, j );p t ≥ then (4.10)elsifN(it ,x) = ∅ thenelsebeginp(it) :=n;Contract(P);Succ(i t ):={ j |p(j) = minp(j) }; (4.11)j ∈N(it,x)Cand(i t ):= {( it,j) A(it,x)| j ∈ Succ(it)}U∈ (4.12)U {( j,it) ∈ A(it,x)| j ∈ Succ(it)};seleziona j∈Succ(n t );p(i t ):= (j) + 1p ; (4.13)if i t = i 1 OR p(pred(i t )) > p (j)then Extend(P, j );else Contract(P);end;end;end;end;procedure Augmentbeginδ= 1min{ e(i ),min{rij | (i, j) ∈ P}};aumenta <strong>di</strong> δ unità il flusso lungo P e termina l’iterazione;end;63


Stabiliamo ora le proprietà <strong>di</strong> base <strong>di</strong> tale algoritmo con il seguente:Teorema 4.10.a) Ogni iterazione dell’algoritmo auction max/flow fino all’in<strong>di</strong>viduazione delcorrispondente augmenting path, consiste <strong>di</strong> un’applicazione dell’algoritmopath construction (seconda versione) alla rete residua, dove il nodo iniziale delpath è il nodo i 1 estratto in tale iterazione, ed il nodo terminale del path è ilnodo pozzo t.b) L’algoritmo termina, ed al termine esiste un taglio saturo che separa il nodopozzo da tutti i no<strong>di</strong> con eccesso maggiore <strong>di</strong> zero. Tale taglio è un tagliominimo.c) Il tempo d’esecuzione dell’algoritmo é O(n 2 m) .Dim. (a) Confrontando le descrizioni dell’algoritmo path construction e l’iterazionedell’algoritmo auction, notiamo che la con<strong>di</strong>zione (4.1) mantenuta vera dall’algoritmo pathconstruction, è equivalente alla con<strong>di</strong>zione (4.5) che deve essere sod<strong>di</strong>sfatta affinché lacoppia (x, p) sia valida. Notiamo ancora che il cambiamento <strong>di</strong> prezzo p(i t )= p ( j)+ 1dell’algoritmo path construction corrisponde al cambiamento <strong>di</strong> prezzo (4.13) ed il testp( it)≥ p(j)corrisponde al test (4.10). Definiamo grafo ammissibile dell’algoritmo maxflowil grafo il cui insieme dei no<strong>di</strong> è N e il cui insieme degli archi è{( i,j)| j ∈ Succ(i)I N(i,x),p(i)≥ p(j),∀ i ∈ N}. Gli insiemi Succ (i)e il grafoammissibile visti nell’algoritmo per la costruzione del cammino, corrispondonorispettivamente all’ insieme Succ( i)I N(i,x)e al grafo ammissibile dell’algoritmo maxflow.Basandoci sulle associazioni precedenti, è possibile notare che se all’inizio <strong>di</strong> unaiterazione dell’algoritmo max-flow il grafo ammissibile è aciclico, allora l’iterazionedell’algoritmo fino all’in<strong>di</strong>viduazione dell’augmenting path è equivalente all’applicazionedell’algoritmo path construction alla rete residua. Dunque per provare il risultato dobbiamomostrare che il grafo ammissibile dell’algoritmo max-flow resta aciclico durantel’esecuzione dell’algoritmo.64


Data la restrizione iniziale p( i)= p(j)+ 1 ∀ j ∈ Succ(i), il grafo ammissibile è aciclicoall’inizio dell’algoritmo. Inoltre, se il grafo ammissibile è aciclico all’inizio <strong>di</strong> unaiterazione, resta tale fino all’in<strong>di</strong>viduazione dell’augmenting path, poiché l’algoritmo pathconstruction preserva l’aciclicità del grafo ammissibile. Dimostriamo che un’operazione <strong>di</strong>aumento non aggiunge archi nuovi al grafo ammissibile, mantenendo la proprietà <strong>di</strong>aciclicità. Supponiamo che un aumento si verifichi lungo il cammino (i 1 , i 2 , …, i k , t) e cheuno degli archi (i m , i m-1 ), con m=2, …, k, sia aggiunto alla rete residua e al grafoammissibile in seguito ad un aumento. Allora, si ha che p ( im)≥ p(im− 1), conim− 1∈ Succ(im)(per definizione <strong>di</strong> grafo ammissibile), e p( im− 1)≥ p(im)coni m ∈ Succ( im− 1) (poiché, l’arco (i m–1 , i m ) appartiene all’ augmenting path), da cuip ( im− 1)= p(im). Questo implica che p ( im − 1)e p ( im)sono stati incrementati almeno unavolta dall’inizio dell’algoritmo (poiché abbiamo p( i)= p(j)+ 1 ∀ j ∈ Succ(i)sia all’iniziodell’algoritmo sia dopo aver ricalcolato l’insieme Succ (i)). Tuttavia, le con<strong>di</strong>zionip ( im− 1 ) < p(im)+ 1 e i m ∈ Succ( im− 1)implicano che l’ultimo incremento <strong>di</strong> p ( im)èaccaduto dopo l’ultima volta che è stato ricalcolato Succ ( im − 1)(questo perché in seguito alcalcolo <strong>di</strong> Succ (i), si ha p ( i)= p(j)+ 1 ∀ j ∈ Succ(i)). Dunque l’ultimo incremento <strong>di</strong>p ( im)ha luogo dopo l’ultimo incremento <strong>di</strong> p ( im − 1)(poiché ogni incremento <strong>di</strong> p (i)comporta il calcolo <strong>di</strong> Succ (i)). Allo stesso modo, le con<strong>di</strong>zioni p ( im)< p(im − 1 ) + 1 eim− 1∈ Succ(im)implicano che vale anche l’opposto, raggiungendo in tal modo unacontrad<strong>di</strong>zione.(b) Dalla parte (a) e dal teorema 4.9, segue che ogni iterazione per ladeterminazione dell’augmenting path termina. Infatti, alla fine <strong>di</strong> una iterazione, si hap(i 1 ) ≥ n, il che ad in<strong>di</strong>care che non esiste nessun augmenting path da i 1 a t, oppure si ha unaumento. Nel primo caso il numero <strong>di</strong> no<strong>di</strong> i con p(i) ≥ n aumenta strettamente, pertantopossono esistere al più n-2 iterazioni <strong>di</strong> questo <strong>tipo</strong>. Per mostrare che il numero <strong>di</strong> aumentiè finito, notiamo che il prezzo <strong>di</strong> ogni nodo può essere incrementato al più n volte, e poichéi prezzi assumono valori interi non negativi, quando il prezzo <strong>di</strong> un nodo eccede n-1 talenodo non verrà più incrementato. Osserviamo che ogni aumento o esaurisce l’eccesso <strong>di</strong> i 1 ,oppure satura almeno un arco. Quando un arco <strong>di</strong> estremi i e j è saturato nella <strong>di</strong>rezione dai a j, si presentano due possibilità: p ( i)= p(j)+ 1, oppure p ( i)= p(j). Nel secondo caso,poiché j ∈ Succ(i)non è possibile che i ∈ Succ( j), altrimenti si violerebbe l’aciclicità del65


grafo ammissibile. In entrambi i casi si ha che uno degli al più n incrementi <strong>di</strong> p ( j)deveverificarsi prima che l’arco possa <strong>di</strong>ventare non saturo e poi saturato ancora nella <strong>di</strong>rezioneda i a j. Così il numero <strong>di</strong> saturazioni <strong>di</strong> un arco è O(n) e il numero totale <strong>di</strong> saturazioni èO(nm), che stabilisce il limite O(nm) sul numero <strong>di</strong> iterazioni e sul numero <strong>di</strong> aumenti. Daciò segue che l’algoritmo termina. Poiché gli aumenti preservano la con<strong>di</strong>zione e(i)≥0 perogni i ≠ s, al termine dell’algoritmo dobbiamo avere e ( i)≥ 0 per ogni i ≠ s, p ( s)= n ,p( i)≥ n per ogni i ≠ t con e ( i)≥ 0 , ep( t)= 0 . Segue che non possono esistereaugmenting path che iniziano dal nodo s o da un nodo i con e ( i)≥ 0 , il che implical’esistenza <strong>di</strong> un taglio saturo [ S , S]tale che s ∈ S , t ∈ S , e( i)≥ 0 ∀ i ≠ s ,e( i)= 0 ∀ i ∈ S,i ≠ t . Come detto in precedenza, tale taglio è un taglio minimo.(c) Nel punto (b) abbiamo <strong>di</strong>mostrato che:1. Esistono al più n incrementi <strong>di</strong> prezzo per un dato nodo.2. Esistono O(nm) iterazioni e O(nm) operazioni <strong>di</strong> aumento.Dal punto 1 precedente, si hanno al più n contrazioni ed estensioni che implicano unaumento del prezzo <strong>di</strong> ogni nodo, ed il tempo per l’estensione e la contrazione èproporzionale al grado <strong>di</strong> i t. Così il tempo totale è O(nm). Inoltre, poiché ogni operazione <strong>di</strong>aumento implica un cambiamento del flusso su ciascuno degli al più n-1 archi, il tempo pergli aumenti è O(n 2 m).Resta da stabilire un limite al tempo necessario alle estensioni che non implicano unaumento del prezzo. Dimostriamo, per assurdo, che ognuna <strong>di</strong> tali estensioni non implicache Succ ( it)debba essere ricalcolato, ovvero, un’estensione <strong>di</strong> questo <strong>tipo</strong> implica ilcalcolo <strong>di</strong> Succ ( it)per la prima volta, oppure implica che il test (4.10) è non sod<strong>di</strong>sfattoper ogni j ∈ Succ( it)I N( it , x). Supponiamo allora che l’insieme Succ ( it)sia ricalcolatocome specificato nell’equazione (4.11) e che valga la con<strong>di</strong>zionep( it)= p(j)+ 1 ∀ j ∈ Succ(it)in modo tale che un’estensione sia effettuata senza alcunincremento <strong>di</strong> p ( it). Allora, ogni j ∈ Succ( it)doveva essere un vicino eleggibile <strong>di</strong> i t ed ilsuo prezzo è rimasto inalterato dall’ultimo calcolo <strong>di</strong> Succ ( it). Ma ciò è unacontrad<strong>di</strong>zione, in quanto affinché Succ ( it)venga ricalcolato, tutti i no<strong>di</strong> j nell’insiemeSucc( it)I N( it , x)devono sod<strong>di</strong>sfare p( it ) < p(j). Dunque se una estensione da i t noncausa un aumento del prezzo p ( it), essa non implica che Succ ( it)debba essere ricalcolato.66


Pertanto, con un’opportuna implementazione <strong>di</strong> Succ ( it), tale estensione richiede tempoO(1), a meno che essa non coinvolga il calcolo <strong>di</strong> Succ ( it)per la prima volta. Il numerototale <strong>di</strong> estensioni è O(n 2 m) perché in ogni iterazione, il numero <strong>di</strong> estensioni eccede ilnumero <strong>di</strong> contrazioni <strong>di</strong> al più n - 1; il numero totale <strong>di</strong> contrazioni durante tuttol’algoritmo è O(n 2 ), mentre il numero totale <strong>di</strong> iterazioni è O(nm). Così, il tempo totale perestensioni che non implicano un aumento del prezzo è O(n 2 m).Costruzione della soluzione ammissibileDato il taglio [ S , S]e il vettore <strong>di</strong> flusso x ottenuto al termine dell’algoritmo, possiamoottenere un massimo flusso applicando un algoritmo che restituisce alla sorgente il flussoin eccesso, che è entrato nella rete ed è stato accumulato su alcuni no<strong>di</strong> <strong>di</strong> S. In particolare,cancelliamo tutti i no<strong>di</strong> in S e tutti gli archi che hanno almeno un estremo appartenente aS ; inoltre, per ogni nodo i∈S, con i ≠ s e tale che∑{( i,j)|j∈S}u ij > 0(4.15)introduciamo un arco (i, s) con flusso e capacità∑x is = uis= uij(4.16){( i,j)|j∈S}(se l’arco (i, s) già esiste, cambiamo la sua capacità ed il flusso ai valori sopraelencati). Nellarete risultante che in<strong>di</strong>chiamo con G, ci poniamo il problema <strong>di</strong> trovare un vettore <strong>di</strong> flusso x′tale che i corrispondenti eccessi siano zero. Si può vedere che gli eccessi corrispondenti alvettore <strong>di</strong> flusso x riferito a G sono uguali agli eccessi non negativi e(i) ottenuti alla finedell’algoritmo per tutti gli i ≠ s. Possiamo così applicare l’algoritmo del massimo flussovisto in questo paragrafo con questo vettore <strong>di</strong> flusso x e vettore dei prezzi p dato da:p ( i)= 0 se i = s, p (i) = “lunghezza <strong>di</strong> uno shortest <strong>di</strong>rect path da i a s” se i ≠ s.La coppia (x, p) è valida per il grafo G. Si può mostrare allora che ogni iterazionedell’algoritmo terminerà con un’operazione <strong>di</strong> aumento su un path che parte da un nodo i cone(i) > 0 e termina al nodo sorgente s. Osserviamo che, dato un qualsiasi vettore <strong>di</strong> flussoammissibile e un nodo i con eccesso positivo, esiste un augmenting path che parte da i etermina su un nodo con eccesso negativo (ve<strong>di</strong> appen<strong>di</strong>ce A.1: decomposizione del flusso).Nel nostro caso il nodo s è il solo nodo con eccesso negativo. In tal modo l’algoritmoterminerà quando gli eccessi <strong>di</strong> tutti i no<strong>di</strong> i ≠ s saranno ridotti a zero, mentre fino al termine67


dell’algoritmo i flussi sugli archi (i, s) saranno uguali al valore iniziale dato dall’equazione(4.16), poiché questi archi non possono far parte <strong>di</strong> un augmenting path. Se x′ ij è il flussofinale <strong>di</strong> ogni arco (i, j) <strong>di</strong> G, si può mostrare, usando anche il fatto che e(i) = 0 per tutti glii ∈ Sx ij * = x′ ij secon i ≠ t, che il vettore <strong>di</strong> flusso x * , così definito:x ij * = x ij altrimenti,i ∉ S , j ∉ Savrà eccesso e(i) * sod<strong>di</strong>sfacente le con<strong>di</strong>zioni e(i) * = 0 per ogni i ≠ s, t, e(s) * < 0 e e(t) * > 0,finché non verrà saturato il taglio [ S , S]. Così, dal teorema del massimo flusso - minimotaglio, x * deve essere un flusso massimo e [ S , S]deve essere un taglio minimo.Varianti dell’algoritmo Auction max/flowDescriviamo in questa sezione <strong>di</strong>verse mo<strong>di</strong>fiche che possono essere apportateall’algoritmo precedente al fine <strong>di</strong> incrementarne le prestazioni. L’algoritmo Auction puòcreare un taglio saturo molto velocemente e sprecare ulteriore tempo dopo aver creato taletaglio per incrementare ad n il prezzo dei no<strong>di</strong> con eccesso positivo. E’ importante,pertanto, utilizzare delle procedure che in<strong>di</strong>viduano velocemente la presenza <strong>di</strong> tagli saturi.Una possibilità è l’euristica gap, descritta nel capitolo 2; un <strong>di</strong>verso approccio è costituitodall’eseguire perio<strong>di</strong>camente una visita in ampiezza a partire dal nodo pozzo, e costruirel’insieme S dei no<strong>di</strong> da ognuno dei quali esiste un <strong>di</strong>rect path che termina al nodo pozzo.Se tutti i no<strong>di</strong> in S hanno eccesso nullo, allora S definisce un taglio minimo. Notiamo chenel costruire S se viene in<strong>di</strong>viduato un nodo con eccesso positivo, la visita in ampiezza puòessere terminata.In una versione alternativa <strong>di</strong> tale schema, può essere effettuato un global repricing, dovein<strong>di</strong>viduiamo tutti i no<strong>di</strong> in S, ovvero tutti i no<strong>di</strong> per cui esiste un <strong>di</strong>rect path verso il pozzo,e rietichettiamo il prezzo <strong>di</strong> ognuno <strong>di</strong> tali no<strong>di</strong> alla lunghezza del più piccolo <strong>di</strong>rect pathdal nodo in considerazione al nodo pozzo. Il prezzo dei no<strong>di</strong> che non sono raggiungibilidurante tale processo sarà settato ad n, in modo tale che saranno esclusi automaticamentedal processo <strong>di</strong> selezione dell’algoritmo. E’ importante associare al global repricingun’euristica che assicura che tale metodo non sia eseguito molto frequentemente, a causadell’associato overhead.68


Nell’algoritmo non è stato specificato il criterio con cui scegliere il nodo con eccessopositivo che sarà il nodo iniziale del path. Una possibilità è scegliere un nodo con il piùgrande prezzo minore <strong>di</strong> n tra i prezzi dei no<strong>di</strong> in N che hanno eccesso positivo.Un’alternativa è mantenere i no<strong>di</strong> con eccesso positivo e prezzo minore <strong>di</strong> n in una codaFIFO, ed estrarre il nodo in testa alla coda.Un ulteriore miglioramento riguarda il flusso spe<strong>di</strong>to lungo l’augmenting path in<strong>di</strong>viduato.Anziché spe<strong>di</strong>re la stessa quantità <strong>di</strong> flusso lungo ogni arco del path, è possibile spe<strong>di</strong>relungo ogni arco (i, j) il massimo possibile, ovvero min{e(i), r ij }. Tale metodo, chechiameremo aumento greedy, consente <strong>di</strong> ovviare all’inconveniente degli algoritmiaugmenting path, <strong>di</strong> cui abbiamo <strong>di</strong>scusso nel paragrafo 3.1.4.4 Algoritmo Auction/BudgetConsideriamo l’algoritmo Auction originale nel momento in cui estrae un nodo coneccesso positivo, <strong>di</strong>ciamo i 1 . Estratto tale nodo, l’algoritmo Auction originale cercherà <strong>di</strong>identificare un augmenting path che inizi da tale nodo ed abbia come nodo terminale ilnodo pozzo. Nell’algoritmo Auction/Budget, associamo al path P che l’algoritmo costruiràa partire da i 1 una quantità positiva definita budget( P i1). La quantità budget( P i1)rappresenta una misura del prezzo che si è <strong>di</strong>sposti a pagare per effettuare un’operazione<strong>di</strong> aumento sul path P che sarà costruito a partire da i 1 . E’ possibile definire budget( P i1)in funzione <strong>di</strong> p(i 1 ), ad esempio k p(i 1 ). Una volta estratto il nodo iniziale i 1 , e calcolatobudget( P i1), l’algoritmo cerca <strong>di</strong> in<strong>di</strong>viduare un augmenting path che inizi da i 1 e sia<strong>di</strong>retto verso il pozzo, senza necessariamente raggiungerlo. Per far ciò, ogni volta chel’algoritmo estende il path su un nodo j decrementa budget( P i1) del valore p(j); quandoinvece l’algoritmo contrae il path eliminando il nodo terminale, esso incrementabudget( P i1) del prezzo del nodo terminale eliminato dal cammino. Quando, nell’estendersisu un nodo l, si ha che budget( P i1) ≤ 0, l’algoritmo aumenta lungo il cammino da i 1 a l.Notiamo come tale tecnica può essere considerata una estensione della logica Auction:noto un path dal nodo estratto i 1 ad un nodo l più vicino al pozzo <strong>di</strong> quanto lo sia i 1 ,inviamo flusso da i 1 ad l senza insistere nel raggiungere il nodo pozzo. Descriviamo oraformalmente l’algoritmo.69


algorithm Auction/Budget(K)begininitialize;while esiste un nodo i 1 ≠ t tale che e(i 1 ) > 0 e p(i 1 ) < n dobeginseleziona un nodo i 1 ≠ t tale che e(i 1 ) > 0 e p(i 1 ) < n;Budget:=kp(i 1 );P:=(i 1 );i t := i 1 ;while p(i 1 ) < n AND i t ≠ t AND Budget > 0beginif esiste j ∈ Succ(it)I N(it ,x)tale che p(it)≥ p(j)thenExtend(P, j );Budget:=Budget - p (j);elsifN(it ,x) = ∅ thenContract(P);Budget:=Budget + p(it ) ;elsebeginp(it) :=n;Succ(i t ):={ j |p(j) = minp(j) };j ∈N(it,x)Cand(i t )= {( it,j) ∈ A(it,x)| j ∈ Succ(it)}U {( j,it) ∈ A(it,x)| j ∈ Succ(it)};seleziona j∈Succ(n t );p(i t ):= p (j) + 1;if i t = i 1 OR p(pred(i t )) > p (j)thenExtend(P, j );end;end;elsebeginend;Budget:=Budget - p (j);Contract(P);Budget:=Budget + p(it ) ;if i t =t OR Budget ≤ 0 then augment;end;end;70


procedure initialize;beginend;x := 0;∀i ∈N p(i)=”lunghezza <strong>di</strong> uno shortest <strong>di</strong>rect path da i a t”;x sj := u sj per ogni arco (s, j) ∈ A(s);p(s) := n;procedure augment;beginfor each ( i, j) ∈ Pbeginend;δ = min{ e(i),rij};Invia δ unità <strong>di</strong> flusso da i a j;end;Calcolo della lunghezza del camminoStabiliamo ora dei limiti alla lunghezza del cammino costruito con la tecnica del budget,supponendo che budget( P i1) = k p(i 1 ), per un generico k <strong>di</strong> cui <strong>di</strong>scuteremo in seguito.Lemma 4.11. Supponiamo che l’algoritmo associ ad un nodo estratto i 1 la quantitàbudget( P i1) = k p(i 1 ). Se denotiamo con l(P) la lunghezza del path costruito con la tecnicadel budget, allora vale la seguente relazione:⎢2( 2 p(i1)−1)− (1 − 2 p(i1))− 8kp(i1)⎥k ≤ l(P) ≤ ⎢⎥ + 1. (4.17)⎢2⎣⎥⎦Dim. Poiché vale la proprietà p( i)≥ p(j)∀ ( i,j)∈P, il path costruito sarà lungoalmeno k. Infatti il path <strong>di</strong> lunghezza minima si ottiene quando budget( P i1) <strong>di</strong>ventanegativo nel minor numero si estensioni, il che si verifica quando in ogni estensione viene71


sottratto il massimo possibile, ovvero p(i 1 ). Dunque per rendere budget( P i1) ≤ 0 occorronoalmeno k estensioni.Consideriamo ora il limite superiore. Il cammino <strong>di</strong> lunghezza massima si ha quandobudget( P i1) <strong>di</strong>venta negativo nel maggior numero <strong>di</strong> estensioni, il che si verifica quandol’algoritmo decrementa budget( P i1) del minimo valore possibile. Sia i t il nodo terminaledel cammino e supponiamo che l’algoritmo estenda il cammino sul nodo j. Poichép( it ) ≤ p(j)+ 1, p(j) è almeno p( it)- 1. Pertanto il cammino <strong>di</strong> lunghezza massima siottiene quando l’algoritmo in corrispondenza delle varie estensioni decrementa budget( P i1)<strong>di</strong> p( i1)-1, poi p( i1)-2 e così via. Quante estensioni può effettuare l’algoritmo prima chebudget( P i1) <strong>di</strong>venti negativo, e dunque terminare la costruzione del path? L’algoritmocontinuerà nell’estendersi finché budget( P i1)>0, cioè finché varrà la con<strong>di</strong>zione:k p(i 1 ) > ( p( i1)-1) + ( p( i1)-2) + … +( p( i1)-x). (4.18)Il più grande intero x per cui vale la con<strong>di</strong>zione precedente in<strong>di</strong>ca la lunghezza del path percui una successiva estensione causerà un aumento, ovvero violerà la con<strong>di</strong>zione (4.18).Conoscendo x possiamo dunque stabilire il limite superiore che stiamo cercando.Riscrivendo <strong>di</strong>versamente la (4.18) otteniamo:xk p(i 1 ) > x p(i 1 ) - ∑ l =l ⇔ k p(i1 1 ) > x p(i 1 ) -x( x +1).22Nell’ipotesi che ∆ = ( 1−2 p(i1))− 8kp(i1)≥ 0, la <strong>di</strong>suguaglianza precedente è sod<strong>di</strong>sfattaper i valori <strong>di</strong> x tali che:( 2 p(i1)−1)− (1 − 2 p(i1))2 − 8kp(i1)x < e (4.19)2( 2 p(i1)−1)+ (1 − 2 p(i1))2 − 8kp(i1)x > .2Notiamo ora che la <strong>di</strong>suguaglianza (4.19) è valida nell’istante in cui l’algoritmo estrae ilnodo i 1 , in quanto si ha x = 0. Il funzionamento dell’algoritmo equivale ad incrementare lalunghezza x del cammino fin quando la (4.18) non sarà più valida, e si verificherà unaumento. Per cui, la seconda soluzione non ha senso dal nostro punto <strong>di</strong> vista. La (4.19)in<strong>di</strong>ca la massima lunghezza del path in corrispondenza della quale una successivaestensione causerà un aumento; considerando anche che la lunghezza del path è un valore72


intero, la lunghezza del path, dopo l’estensione che ha causa l’aumento è limitata⎢2( 2 p(i1)−1)− (1 − 2 p(i1))− 8kp(i1)⎥superiormente da ⎢⎥ + 1.⎢2⎣⎥⎦Se invece ∆ < 0 , la <strong>di</strong>suguaglianza precedente è sempre sod<strong>di</strong>sfatta. In questo caso, che siverifica scegliendo un k opportunamente grande, l’algoritmo eseguirà l’operazione <strong>di</strong>aumento solo quando il path si estenderà sul pozzo, in quanto budget( P i1) non <strong>di</strong>venteràmai negativo.A titolo d’esempio, in<strong>di</strong>viduiamo il limite superiore alla lunghezza del path nell’ipotesi chevenga estratto un nodo <strong>di</strong> prezzo 8 e che k sia uguale a 2. Dalla (4.19) essendo x intero,otteniamo x ≤ 2, il che in<strong>di</strong>ca che se il cammino è lungo 2, una successiva estensionerenderà il budget negativo o nullo, e occorrerà un’operazione <strong>di</strong> aumento. Ricor<strong>di</strong>amoinfatti che la (4.19) in<strong>di</strong>ca il più grande valore <strong>di</strong> x per cui budget( P i1)>0; <strong>di</strong> conseguenzail limite superiore alla lunghezza del cammino è 3, che si ottiene <strong>di</strong>rettamente dalla (4.16).Preflow-<strong>push</strong> e augmenting path come casi particolari dell’Auction/BudgetL’algoritmo Auction/Budget può essere considerato una generalizzazione degli approcciaugmenting path e <strong>preflow</strong>-<strong>push</strong>, che possono essere ottenuti da un’opportuna scelta <strong>di</strong>budget( P i1). Consideriamo innanzitutto l’algoritmo <strong>preflow</strong>-<strong>push</strong> e mostriamo come puòessere ottenuto dall’algoritmo Auction/Budget in cui utilizziamo l’aumento greedy.Entrambi gli algoritmi Auction/Budget e <strong>preflow</strong>-<strong>push</strong>, iniziano col saturare gli archiuscenti dalla sorgente. Da questo punto in poi, gli algoritmi si <strong>di</strong>fferenziano, infattil’algoritmo <strong>preflow</strong>-<strong>push</strong> selezionerà no<strong>di</strong> attivi ed in<strong>di</strong>viduerà archi downhill su cuiinviare flusso, mentre l’algoritmo Auction/Budget dopo aver selezionato un nodo attivo,cercherà <strong>di</strong> in<strong>di</strong>viduare degli augmenting path. Ipotizziamo che il criterio <strong>di</strong> selezione deino<strong>di</strong> attivi sia lo stesso in entrambi gli algoritmi. Per ridurre l’algoritmo Auction/Budget aquello <strong>preflow</strong>-<strong>push</strong>, basta definire budget( P i1)=k p(i 1 ), e scegliere k = 0. In questo modo,una volta selezionato i 1 la successiva estensione causerà l’esecuzione della procedura <strong>di</strong>aumento su un path <strong>di</strong> lunghezza 1. Per quel che riguarda l’approccio augmenting path,consideriamo l’algoritmo Auction/Budget in cui evitiamo l’aumento greedy. Per far in73


modo che l’algoritmo Auction/Budget esegua l’operazione <strong>di</strong> aumento solo quando il pathha raggiunto il nodo pozzo, possiamo definire ancora budget( P i1)=k p(i 1 ) e scegliere k inmodo tale che la con<strong>di</strong>zione (4.18) sia sempre verificata. A tal fine, è sufficiente scegliere k22in modo tale che ∆ = ( 1− 2 p(i1))− 8kp(i1)< 0, ovvero k > ( 1− 2 p(i1))/8 p(i1).La scelta migliore <strong>di</strong> k <strong>di</strong>pende dal problema in considerazione. Abbiamo infatti trovatosperimentalmente che un determinato valore <strong>di</strong> k può essere ottimo per un determinato <strong>tipo</strong><strong>di</strong> problema e non buono per un altro. Come si evince dai risultati sperimentali nelsuccessivo capitolo, un buon compromesso è una scelta <strong>di</strong> k nell’intervallo [3,10].Correttezza e complessitàAssumendo che l’algoritmo Auction/Budget termini, è semplice mostrare che essoin<strong>di</strong>vidua un massimo flusso. L’algoritmo termina quando gli eccessi risiedono solo nelnodo sorgente o nel nodo pozzo, il che implica che al termine dell’algoritmo il <strong>preflow</strong> èun flusso. Poiché p(s) = n, la rete residua non contiene <strong>di</strong>rect path dal nodo sorgente alnodo pozzo, il che, per il teorema 2.1, implica che il flusso è massimo.Dimostriamo ora che l’algoritmo termina, notandoche ogni iterazione per ladeterminazione dell’augmenting path parziale termina. Infatti, alla fine <strong>di</strong> una iterazione, siha p(i 1 ) ≥ n, il che ad in<strong>di</strong>care che non esiste nessun augmenting path da i 1 a t, oppure si haun aumento verso il nodo pozzo, o verso un nodo che <strong>di</strong>sta almeno k dal nodo i 1 (per illemma 4.11). Nel primo caso il numero <strong>di</strong> no<strong>di</strong> i con p(i) ≥ n aumenta strettamente,pertanto possono esistere al più n-2 iterazioni <strong>di</strong> questo <strong>tipo</strong>. Per mostrare che il numero <strong>di</strong>aumenti è finito, notiamo che il prezzo <strong>di</strong> ogni nodo può essere incrementato al più n volte,e poiché i prezzi assumono valori interi non negativi, quando il prezzo <strong>di</strong> un nodo ecceden-1 tale nodo non verrà più incrementato. Osserviamo che ogni aumento o esauriscel’eccesso <strong>di</strong> i 1 , oppure satura almeno un arco. Quando un arco <strong>di</strong> estremi i e j è saturatonella <strong>di</strong>rezione da i a j, si presentano due possibilità: p ( i)= p(j)+ 1, oppure p ( i)= p(j).Nel secondo caso, poiché j ∈ Succ(i)non è possibile che i ∈ Succ( j), significherebbe cheil path parziale è un ciclo . In entrambi i casi si ha che uno degli al più n incrementi <strong>di</strong>p ( j) deve verificarsi prima che l’arco possa <strong>di</strong>ventare non saturo e poi saturato ancoranella <strong>di</strong>rezione da i a j. Così il numero <strong>di</strong> saturazioni <strong>di</strong> un arco è O(n) e il numero totale <strong>di</strong>74


saturazioni per ogni arco è O(nm), che stabilisce il limite O(nm) sul numero <strong>di</strong> iterazioni esul numero <strong>di</strong> aumenti. Da ciò segue che l’algoritmo termina.Per quel che riguarda il tempo d’esecuzione, vale la stessa analisi effettuata nel Teorema4.10 (c), per cui il tempo d’esecuzione dell’algoritmo è O(n 2 m).75

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!