20.05.2013 Views

IL PROBLEMA DEL FLUSSO DI COSTO MINIMO - TWiki

IL PROBLEMA DEL FLUSSO DI COSTO MINIMO - TWiki

IL PROBLEMA DEL FLUSSO DI COSTO MINIMO - TWiki

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

<strong>IL</strong> <strong>PROBLEMA</strong> <strong>DEL</strong> <strong>FLUSSO</strong> <strong>DI</strong> <strong>COSTO</strong> <strong>MINIMO</strong><br />

DANIEL BUCCARELLA – 698102 – danielbuccarella@virgilio.it


1. Definizione del Problema<br />

Spesso i problemi di ottimizzazione sono caratterizzati da una struttura di grafo. In<br />

alcuni casi questa struttura nasce dal particolare modo in cui un problema viene<br />

modellato, in altri emerge in modo del tutto naturale. Si pensi, ad esempio, ad una<br />

rete stradale. Essa è rappresentabile naturalmente come un grafo in cui i nodi sono gli<br />

incroci e gli archi le strade.<br />

Nel seguito studieremo un problema di base definito su reti, enunciandone le<br />

proprietà e introducendo un semplice algoritmo risolutivo.<br />

Con il termine “rete” indichiamo un grafo G= N , A orientato e pesato. Ai nodi e<br />

agli archi del grafo, infatti, sono associati dei valori numerici chiamati, appunto,<br />

“pesi”.<br />

Possiamo interpretare gli archi della rete come canali attraverso cui fluiscono dei<br />

beni, rappresentabili per mezzo di grandezze discrete (pensiamo al numero di<br />

messaggi attraverso una rete di comunicazione) o continue (quantità di petrolio che<br />

fluisce in un oleodotto). I beni, inoltre, possono rappresentare valori assoluti oppure<br />

valori relativi, ad esempio, ad unità di tempo.<br />

Proviamo a pensare ai pesi dei nodi come la quantità dei beni che in quei nodi<br />

entrano nella rete o ne escono. Ad ogni nodo i∈N è associato un valore reale b i<br />

(bilancio o afflusso netto nel nodo), che può essere:<br />

● Positivo: b i viene detto domanda del nodo, in quanto rappresenta la quantità<br />

del bene che esce dalla rete al nodo i . Il nodo viene detto destinazione, pozzo<br />

o nodo di output.<br />

● Negativo: ­ b i viene detto offerta del nodo, in quanto rappresenta la quantità<br />

del bene che entra nella rete al nodo i . Il nodo viene detto origine, sorgente o<br />

nodo di input.<br />

● Nullo: i viene detto nodo di trasferimento.<br />

Pensando invece ai pesi degli archi come capacità e costi abbiamo che ad ogni arco<br />

a =i , j k sono associati un costo ck (o cij ), indicante il costo che viene pagato per<br />

ogni unità del bene che attraversi l'arco, ed una capacità inferiore l k ( lij ) e<br />

superiore uk ( uij ), indicanti rispettivamente il minimo e massimo numero di unità di<br />

bene che possono attraversare l'arco.<br />

Un flusso ammissibile in una rete è una funzione f : N ×N R che assegna ad ogni<br />

arco i , j un valore reale xij che soddisfa i seguenti requisiti:


● Requisito di Capacità: ∀ i , j∈N , l ij ≤x ij ≤u ij<br />

● Requisito di Simmetria: ∀ i , j∈N , x ij = ­ x ji<br />

● Requisito di Bilancio: ∀ i∈N ,∑ j ∈ N x ij =b i<br />

Il bilancio rappresenta la differenza tra il flusso entrante e il flusso uscente da<br />

un nodo; se il bilancio di un nodo i è nullo ( b i =0 ), si parla di equazione di<br />

conservazione del flusso in quanto, in questo caso, il nodo produce la stessa<br />

quantità di flusso che consuma.<br />

Nei problemi di flusso la somma di tutte le domande (domanda globale) è uguale alla<br />

somma, cambiata di segno, di tutte le offerte (offerta globale). Definiti, cioè, gli<br />

insiemi D dei nodi di domanda e O dei nodi di offerta:<br />

si ha che:<br />

D={i∈N :b i 0},O={i∈N : b i 0},<br />

∑ i ∈D b i = ­ ∑ i ∈O b i<br />

Da ciò è facile dedurre che ∑ i ∈N b i =0 .<br />

Il costo di un flusso ammissibile è definito come:<br />

costox= ∑ c x ij ij<br />

i , j∈ A<br />

Il problema del flusso di costo minimo: è data una rete G= N , A , con ∣A∣=m . Ad<br />

ogni arco i , j∈A sono associati un costo c ij , una capacità inferiore l ij =0 ed una<br />

capacità superiore u ij . Ad ogni nodo i∈N è associato un bilancio b i . Intendiamo<br />

trovare, fra tutti i flussi ammissibili in G , il flusso (un vettore x∈R m contenente i<br />

valori del flusso su ogni arco) che rende minimo costox .


2. Condizioni di Ottimo<br />

Lo studio delle condizioni di ottimo, cioè delle proprietà che ci permettono di<br />

verificare se una data soluzione è ottima per il problema, costituisce il primo passo<br />

per lo sviluppo di un algoritmo. Introduciamo quindi i concetti che ci torneranno utili<br />

durante la trattazione.<br />

Un vettore x∈R m che rispetta tutti i vincoli di capacità sugli archi, cioè tale che<br />

0≤x≤u , è detto pseudoflusso.<br />

Lo sbilanciamento di un nodo i rispetto a x è la quantità<br />

Definiamo poi<br />

e x i=∑ j ∈N x ij ­ b i<br />

O x ={i∈N : e x i0}, D x ={i∈N :e x i0}<br />

rispettivamente l'insieme dei nodi con eccedenza di flusso e quello dei nodi con<br />

difetto di flusso. Se risulta D x =O x =∅ , se tutti i nodi sono, cioè, bilanciati, il vettore<br />

x rispetta il Requisito di Bilancio.<br />

Indicando con e x il vettore degli sbilanciamenti del vettore x ,<br />

g x=∑ i ∈Ox<br />

e x i<br />

rappresenta lo sbilanciamento complessivo, calcolabile anche con la formula<br />

gx = ­ ∑ j∈ Dx<br />

e x j<br />

Possiamo dunque affermare che x rispetta il Requisito di Bilancio se e solo se<br />

g x=0 .<br />

Dato un qualsiasi cammino P tra una qualunque coppia di nodi s e t del grafo,<br />

consideriamo il verso di P quello che va da s a t . Gli archi del cammino<br />

risultano, quindi, partizionati nei due insiemi degli archi concordi ( P ) e discordi<br />

( P ) col verso del cammino. La capacità del cammino P rispetto al flusso x è<br />

data da:


P , x=min { min { u ij ­ x ij :i , j∈P <br />

} , min { x ij :i , j∈P <br />

Questa formula rappresenta la quantità massima di flusso che non produce flussi<br />

maggiori delle capacità se aggiunta agli archi di P e non produce flussi negativi se<br />

sottratta agli archi di P . Se accade che almeno uno degli archi di P è saturo<br />

oppure che almeno uno degli archi di P è vuoto, allora P , x=0 . Se, invece,<br />

P , x0 , ciò significa che lungo P può essere inviata una quantità positiva di<br />

flusso da s verso t . P In questo caso è detto cammino aumentante.<br />

Dato uno pseudoflusso x , è possibile inviare lungo P una quantità di flusso<br />

0≤P , x mediante l'operazione di composizione (⊕) nel modo seguente:<br />

x ij ={<br />

x<br />

ij + , sei , j∈P <br />

x ij ­ , sei , j∈P <br />

x ij ,altrimenti<br />

Lo pseudoflusso ottenuto ( x=x ⊕ P ) è tale che:<br />

e x i ={<br />

e<br />

x s ­ , sei=s<br />

e x t + , sei=t<br />

e x i,altrimenti<br />

Inviare flusso lungo un cammino aumentante modifica, quindi, solo lo<br />

sbilanciamento dei nodi estremi del cammino, lasciando invariato quello di tutti i<br />

nodi intermedi.<br />

Il caso in cui s=t è un caso particolare di cammino aumentante: il cammino è, cioè,<br />

un ciclo aumentante C su cui fissiamo arbitrariamente un verso di percorrenza.<br />

L'invio di flusso attraverso un tale cammino aumentante, non modificherebbe,<br />

ovviamente, lo sbilanciamento di alcun nodo. In particolare, a partire da un flusso<br />

ammissibile x , l'operazione di composizione con un ciclo aumentante C permette<br />

di costruire un diverso flusso ammissibile. In altre parole, se x è un flusso<br />

ammissibile, allora, per 0≤≤C , x , ogni flusso x=x ⊕ C è ancora un flusso<br />

ammissibile.<br />

Il costo di un cammino (o di un ciclo) P è il costo di un'unità di flusso inviata,<br />

secondo il verso fissato, lungo P :<br />

}}


c P=∑ i , j∈P c ij ­ ∑ i , j∈P c ij<br />

mentre il costo del nuovo pseudoflusso è dato da<br />

(1) cx=cx ⊕ P=cxc P<br />

Per determinare cammini o cicli aumentanti, si può utilizzare il grafo residuo<br />

G x =N , A x rispetto allo pseudoflusso x : per ogni arco i , j∈A poniamo i , j in<br />

A x , con costo c ij '=c ij , se e solo se x ij u ij , mentre poniamo j , i in A x , con costo<br />

c ji ' = ­ c ji , se e solo se x ij 0 .<br />

In base a questa costruzione, comunque si fissino s e t , per ogni cammino<br />

aumentante da s a t rispetto a x in G , esisterà uno e un solo cammino orientato<br />

da s a t in G x . Il cammino in G e quello in G x avranno lo stesso costo.<br />

Teorema 1: Dati due pseudoflussi qualunque x ' ed x ' ' , esistono k≤nm<br />

cammini o cicli aumentanti per x ' , P 1 , ... , P k , di cui al più m sono cicli, tali che<br />

x 1 =x ' , x i1 =x i ⊕ i P i , per i=1, ... , k , x k1 = x' ' , dove 0 i = P i , x i ,i=1, ... , k . In<br />

particolare, gli estremi di tutti i cammini aumentanti sono nodi in cui lo<br />

sbilanciamento di x ' è diverso dallo sbilanciamento di x ' ' , per cui se x ' ed x ' '<br />

hanno lo stesso vettore di sbilanciamenti, allora tutti i P i sono cicli.<br />

Dimostrazione: la nostra dimostrazione è di tipo costruttivo: manteniamo uno<br />

pseudoflusso x , inizialmente pari a x ' , e lo rendiamo uguale a x ' ' , utilizzando<br />

cammini e cicli aumentanti per x ' , in un numero finito di passi.<br />

' '<br />

={i , j: xij xij } e<br />

<br />

Per far questo definiamo il grafo G = N , A , Ax x x , dove Ax ' '<br />

A ={ j ,i: xij xij } x<br />

. Possiamo affermare che G x descrive la differenza tra x e x ' ' .<br />

<br />

E', infatti, immediato verificare che A = Ax =∅ x se e solo se x ' '=x . Associamo ora<br />

<br />

ad ogni arco i , j∈A x<br />

' '<br />

la quantità d i , j=x x ij<br />

' '<br />

quantità d j ,i=x x ij ­ x 0 ij e definiamo gli insiemi<br />

<br />

­ x 0 ij e ad ogni arco j , i∈A x<br />

O x ={i∈N : e x ie x '' i}, D x ={i∈N :e x ie x ' ' i}<br />

contenenti rispettivamente i nodi che hanno sbilanciamento rispetto a x maggiore<br />

dello sbilanciamento rispetto a x ' ' e i nodi per i quali avviene l'opposto.<br />

Avviene allora che:<br />

la


●<br />

O x =D x =∅ se e solo se x ed x ' ' hanno lo stesso vettore di sbilanciamento<br />

● tutti i nodi in O x hanno almeno un arco uscente in G x , tutti i nodi in D x<br />

hanno almeno un arco entrante in G x , mentre tutti i nodi in N ∖O x ∪D x o<br />

non hanno né archi entranti né archi uscenti oppure hanno sia almeno un arco<br />

entrante che almeno un arco uscente.<br />

Abbiamo ora la possibilità di costruire iterativamente i cicli e i cammini richiesti,<br />

<br />

utilizzando G x . Se A = Ax =∅ x , ossia x= x' ' , il procedimento termina, altrimenti<br />

prendiamo in considerazione i due insiemi O x e D x : se O ≠∅ x selezioniamo un<br />

nodo s∈O x , altrimenti, per costruire un ciclo, selezioniamo un qualsiasi nodo s che<br />

abbia almeno un arco uscente (e quindi almeno uno entrante). Visitiamo, dunque,<br />

G x a partire da s , nodo che ha sicuramente un arco uscente. Siccome, poi, ogni<br />

nodo, tranne al più quelli in Dx , ha almeno un arco uscente, in un numero finito di<br />

passi, la visita:<br />

● o raggiunge un nodo t∈D x<br />

● oppure torna su un nodo già precedentemente visitato.<br />

Nel primo caso determiniamo un cammino P in G x tra un nodo s∈O x e un nodo<br />

t∈D x . Su questo cammino viene inviata una quantità di flusso pari a<br />

=min { min { d x i , j:i , j∈P } , e x s ­ e x ' ' s ,e x '' t ­ e x t }<br />

Nel secondo caso determiniamo un ciclo C in G x . Su questo ciclo viene inviata una<br />

quantità di flusso pari a<br />

=min {d x i , j:i , j∈C }<br />

In entrambi i casi otteniamo un nuovo pseudoflusso x che potremmo definire “più<br />

simile” ad x ' ' rispetto al precedente, in quanto, per ogni i , j di P (o C ),<br />

d x i , j diminuisce della quantità 0 . In particolare, se si determina un cammino,<br />

si avrà che o d x i , j=0 per almeno un i , j∈P , oppure lo sbilanciamento rispetto<br />

ad x di almeno uno tra s e t diventa pari allo sbilanciamento rispetto ad x ' ' , e<br />

quindi almeno uno tra s e t non comparirà più in O x o in D x ; se invece si<br />

determina un ciclo, allora si avrà d x i , j=0 per almeno un i , j∈C , e quindi tale<br />

arco non comparirà più in G x .


E' da notare che il flusso può solo aumentare sugli archi di A x ' , mentre può solo<br />

<br />

' '<br />

diminuire sugli archi di A x . Non appena x =x ij ij , il flusso su i , j non viene più<br />

modificato, perciò in G x non può essere creato nessun nuovo arco.<br />

Possiamo allora concludere che ogni grafo G x è un sottografo del grafo residuo<br />

iniziale G x ' , e perciò un qualunque cammino o ciclo che viene utilizzato è<br />

aumentante rispetto allo pseudoflusso iniziale x ' .<br />

Ad ogni passo cancelliamo o almeno un nodo da O ∪D x x o almeno un aro da G x ,<br />

quindi, tutti gli archi di G x vengono cancellati in al più nm passi. ◊<br />

Il Teorema appena dimostrato ci permette di dare una caratterizzazione degli<br />

pseudoflussi ottimi e quindi dei flussi ottimi. Definiamo minimale uno pseudoflusso<br />

x di costo minimo tra tutti gli pseudoflussi aventi lo stesso vettore di<br />

sbilanciamento e x . La soluzione ottima al nostro problema del flusso di costo<br />

minimo è un flusso ammissibile minimale, un flusso, cioè, che ha costo minimo tra<br />

tutti gli pseudoflussi aventi e x =0 .<br />

Lemma 1: Uno flusso ammissibile (pseudoflusso) x è ottimo (minimale) se e solo<br />

se non esistono cicli aumentanti rispetto ad x il cui costo sia negativo.<br />

Dimostrazione: Se esiste un ciclo aumentante C rispetto ad x il cui costo cC è<br />

negativo, allora x non è minimale: per ogni 0≤C , x , lo pseudoflusso<br />

x=x ⊕ C ha lo stesso vettore di sbilanciamento di x , ma, ricordando (1),<br />

cxcx .<br />

Viceversa, sia x uno pseudoflusso tale che non esistono cicli aumentanti di costo<br />

negativo rispetto ad x . Supponiamo che esista uno pseudoflusso x ' con lo stesso<br />

vettore di sbilanciamento tale che cx 'cx , ossia supponiamo che x non sia<br />

minimale. Allora per il Teorema 1:<br />

x '=x ⊕ 1 C 1 ⊕ ... ⊕ k C k<br />

dove C i sono cicli aumentanti per x . Ma i i sono tutti numeri positivi, quindi, il<br />

fatto che sia cx 'cx e (1) implicano che cC i 0 per un qualche i , il che<br />

contraddice l'ipotesi.


3. Soluzione Basata su Cammini Minimi Successivi<br />

L'algoritmo basato su cammini minimi successivi sfrutta l'idea seguente: ad ogni<br />

passo mantiene uno pseudoflusso minimale x e per diminuire lo sbilanciamento di<br />

x , al minor costo possibile, determina un cammino aumentante di costo minimo tra<br />

un nodo s∈O x e un nodo t∈D x . La minimalità degli pseudoflussi è conservata<br />

grazie all'utilizzo di cammini aumentanti di costo minimo, come dimostra il seguente<br />

Teorema 2: Sia x uno pseudoflusso minimale e, tra tutti i cammini che uniscono un<br />

dato nodo s∈O x ad un dato nodo t∈D x , sia P il cammino aumentante rispetto a<br />

x di costo minimo. In qualsiasi modo venga scelto ≤ P , x, x ⊕ P è uno<br />

pseudoflusso minimale.<br />

Dimostrazione: Fissato ≤ P , x , sia x ' uno pseudoflusso qualsiasi e sia e x il<br />

suo vettore di sbilanciamento. Esistono, per il Teorema 1, k cammini aumentanti<br />

P 1 , ... , P k da s a t (essendo s e t gli unici nodi per i quali differiscono e x ed<br />

e x ) e h cicli aumentanti C 1 , ... ,C h rispetto ad x tali che<br />

x '=x ⊕ 1 P 1 ⊕ ... ⊕ k P k ⊕ k1 C 1 ⊕ ... ⊕ k h C h<br />

Inoltre, deve sicuramente essere 1 ... k = . x è minimale, perciò ciasuno degli<br />

h cicli aumentanti deve avere costo non negativo; inoltre P ha costo minimo tra<br />

tutti cammini aumentanti tra s e t , quindi si ha cP≤c P i ,i=1, ... , k . Allora:<br />

cx '=cx 1 cP 1 ... k c P k k1 cC 1 ... k h cC h ≥cx c P=cx<br />

Quindi, x è minimale. ◊<br />

Ricordiamo che e x s0 e che e x t 0 . L'operazione di composizione tra lo<br />

pseudoflusso x e il cammino P permette, con un'opportuna scelta di , di<br />

diminuire lo sbilanciamento complessivo. Infatti per<br />

(2) =min { P , x , e x s, ­ e x t } > 0<br />

x è uno pseudoflusso minimale con sbilanciamento complessivo<br />

g x=g x ­ gx . Tale scelta di corrisponde alla maggiore diminuzione


possibile dello sbilanciamento complessivo corrispondente al cammino P e allo<br />

pseudoflusso x .<br />

L'algoritmo, mostrato qui di seguito, termina se lo pseudoflusso minimale corrente ha<br />

sbilanciamento nullo (è un flusso ottimo), oppure se non esistono più cammini<br />

aumentanti tra nodi in O x e nodi in D x (il problema non ha soluzioni ammissibili).<br />

Algoritmo camminiMinimiSuccessivi( G , c , b , u , x , caso )<br />

{<br />

inizializza( x );<br />

caso := “ottimo”;<br />

while ( g x != 0) and ( caso != “vuoto”) do<br />

if trovaCamminoMinimo( G x , O x , D x , P , )<br />

then aumentaFlusso( x , P , );<br />

else caso := “vuoto”;<br />

}<br />

Partendo dal presupposto che non esistano archi con costo negativo e capacità<br />

infinita, la procedura inizializza costruisce uno pseudoflusso x minimale nel<br />

modo seguente:<br />

x ij ={<br />

0,<br />

se c ij ≥0<br />

u ij , altrimenti<br />

I costi degli archi in G x risultano, così, tutti non negativi e perciò non esistono cicli<br />

orientati in G x (e cioè cicli aumentanti rispetto a x in G ) di costo negativo. x è<br />

allora minimale.<br />

La procedura trovaCamminoMinimo risolve il problema dell'albero dei cammini<br />

minimi con insieme di nodi radice O x su G x , determinando, quindi, un cammino<br />

aumentante P di costo minimo da un qualsiasi nodo s∈O x a un qualsiasi nodo<br />

t∈D x . Se, cioè, ∣O x∣1 , essa aggiunge a G x un nodo “radice” r collegato con<br />

archi a costo nullo a tutti i nodi in O x e risolve il problema dell'albero dei cammini<br />

minimi di radice r sul grafo così ottenuto; altrimenti utilizza come radice l'unico<br />

nodo in O x . Se la procedura restituisce falso, il nostro algoritmo restituisce “vuoto”<br />

(non esiste nessuna soluzione ammissibile per il nostro problema). In caso contrario,<br />

la procedura restituisce un cammino aumentante P che unisce un nodo s∈O x a un<br />

nodo t∈D x insieme alla quantità di flusso , definita in (2), che deve essere inviata


lungo P , invio del quale si occupa la procedura aumentaFlusso implementando<br />

l'operazione di composizione ⊕.<br />

Se =e s x , allora il nodo s risulterà bilanciato<br />

rispetto al nuovo flusso, e lo stesso discorso vale per il nodo t se = ­ e t x ;<br />

altrimenti, è determinato dalla capacità del cammino e ciò vuol dire che almeno<br />

un arco di P diviene vuoto oppure saturo.<br />

Se l'algoritmo descritto termina con gx=0 , allora x è un flusso ottimo. Per il<br />

Teorema 2, infatti, ad ogni passo lo pseudoflusso x è minimale in quanto l'algoritmo<br />

usa sempre cammini aumentanti di costo minimo. Nel caso in cui b e u siano interi<br />

possiamo facilmente provare la terminazione dell'algoritmo. Lo pseudoflusso iniziale,<br />

in questo caso, è anch'esso intero, e lo sono, quindi, la quantità a quell'iterazione e<br />

lo pseudoflusso x ottenuto al termine dell'iterazione. Ad ogni iterazione, di<br />

conseguenza, x è intero, ≥1 e g x diminuisce almeno di un'unità. L'algoritmo<br />

termina, perciò, in un numero finito di iterazioni. Da quanto detto segue il<br />

Teorema 3: Per qualsiasi scelta dei costi degli archi, se le capacità degli archi ed i<br />

bilanci dei nodi sono interi, allora esiste almeno una soluzione ottima intera per il<br />

problema MCF.<br />

Lo sbilanciamento complessivo dello pseudoflusso x costruito dalla procedura<br />

inizializza è limitato superiormente da g=∑ u ∑ b ij i . Il numero delle<br />

cij0 bi0 iterazioni non potrà eccedere g dato che g x diminuisce di almeno un'unità ad<br />

ogni iterazione. Inoltre, si vede chiaramente che tutte le operazioni effettuate durante<br />

una singola iterazione hanno complessità al più On , esclusa l'invocazione della<br />

procedura trovaCamminoMinimo. Se per tale procedura utilizziamo un algoritmo<br />

di costruzione dell'albero dei cammini minimi che ha complessità Omn , la<br />

complessità totale di camminiMinimiSuccessivi risulta essere Ogmn .


Data l'istanza del problema sopra mostrata, descriviamo, con l'aiuto della prossima<br />

figura, il funzionamento dell'algoritmo basato su cammini minimi successivi.<br />

Tutti i costo sono non negativi, quindi la procedura inizializza costruisce uno<br />

pseudoflusso iniziale identicamente nullo. Mostriamo, per ogni iterazione, sulla<br />

sinistra il grafo residuo G x e a destra lo pseudoflusso ottenuto al termine<br />

dell'iterazione. Nel grafo residuo risulta evidenziato l'albero dei cammini minimi con<br />

i valori delle corrispondenti etichette e viene mostrato il valore del flusso inviato<br />

lungo il relativo cammino aumentante da 1 a 5. I valori del flusso e gli sbilanciamenti<br />

non visualizzati sono da considerare pari a zero.<br />

Al termine della quarta iterazione tutti i nodi hanno sbilanciamento nullo e la<br />

soluzione è, perciò, ottima.


4. Bibliografia<br />

● R. K. Ahuja, T. L. Magnanti, J. B. Orlin ­ “Network flows. Theory, algorithms<br />

and applications” ­ Prentice Hall – 1993<br />

● M. S. Bazaraa, J. J. Jarvis, H. D. Sherali ­ “Linear programming and network<br />

flows” ­ Wiley ­ 1990

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

Saved successfully!

Ooh no, something went wrong!