Push-relabel suurin virtausalgoritmi - Push–relabel maximum flow algorithm
In matemaattisen optimoinnin , push-nimetä uudelleen algoritmia (vaihtoehtoisesti esivirtausaika-push-algoritmi ) on algoritmi, joka laskee maksimi virtaa , joka virtaa verkkoon . Nimi "push-relabel" tulee kahdesta algoritmissa käytetystä perustoiminnosta. Koko sen toteuttamista, algoritmi ylläpitää "esivirtausaika" ja vähitellen muuntaa sen maksimivirtaus siirtämällä virtaus paikallisesti vierekkäisten solmujen käyttämällä push- toimintojen ohjauksessa hyväksyttävän verkon ylläpitämä nimetä uudelleen toimintaan. Vertailun vuoksi Ford – Fulkerson-algoritmi suorittaa maailmanlaajuisia lisäyksiä, jotka lähettävät virtauksen seuraavia polkuja lähteestä aina pesualtaaseen.
Push-relabel-algoritmia pidetään yhtenä tehokkaimmista maksimivirta-algoritmeista. Geneerisellä algoritmilla on voimakkaasti polynominen O ( V 2 E ) -aikakompleksisuus, joka on asymptoottisesti tehokkaampaa kuin O ( VE 2 ) Edmonds – Karp -algoritmi . Algoritmien tietyillä muunnoksilla saavutetaan vielä pienemmät aikakompleksit. Korkeimpaan tarrasolmun valintasääntöön perustuvalla muunnoksella on O ( V 2 √ E ) -aikakompleksisuus ja sitä pidetään yleensä maksimivirta-algoritmien vertailukohtana. Subkubisen O ( VE log ( V 2 / E )) -aikakompleksisuus voidaan saavuttaa dynaamisilla puilla , vaikka käytännössä se on vähemmän tehokasta.
Push-relabel-algoritmia on laajennettu pienimpien kustannusvirtojen laskemiseksi . Etäisyystarrojen idea on johtanut tehokkaampaan polkujen algoritmiin, joka puolestaan voidaan sisällyttää takaisin push-relabel -algoritmiin, jotta voidaan luoda muunnelma, jolla on vielä korkeampi empiirinen suorituskyky.
Historia
Preflow- konseptin on alun perin suunnitellut Alexander V. Karzanov, ja se julkaistiin vuonna 1974 julkaisussa Soviet Mathematical Dokladi 15. Tämä esivirtausalgoritmi käytti myös työntöoperaatiota ; se käytti kuitenkin etäisyyksiä apuverkossa määrittääkseen, mihin virtaus työnnetään merkintäjärjestelmän sijaan.
Push-relabel-algoritmin ovat suunnitelleet Andrew V.Goldberg ja Robert Tarjan . Algoritmi esiteltiin alun perin marraskuussa 1986 julkaisussa STOC '86: Proceedings of the 18th Annual ACM symposium on Theory of computing, ja sitten virallisesti lokakuussa 1988 artikkelina Journal of the ACM: ssä. Molemmissa julkaisuissa kuvataan yleinen muoto algoritmista, joka päättyy O: lla ( V 2 E ), yhdessä O ( V 3 ) -sekvenssin toteutuksen, O ( VE log ( V 2 / E )) -toteutuksen, jossa käytetään dynaamisia puita, ja rinnakkainen / hajautettu toteutus . Goldberg-Tarjanissa selitetty esitteli etäisyysetiketit sisällyttämällä ne Yossi Shiloachin ja Uzi Vishkinin rinnakkaiseen maksimivirtausalgoritmiin .
Käsitteet
Määritelmät ja merkinnät
Päästää:
- G = ( V , E ) on verkko, jonka kapasiteettifunktio c : V × V → ℝ ∞ ,
- F = ( G , c , s , t ) virtaus verkon , jossa s ∈ V ja t ∈ V on valittu lähde ja pesuallas kärjet vastaavasti,
- f : V × V → ℝ tarkoittavat esivirtaus in F ,
- x f : V → ℝ tarkoittaa ylimääräistä funktiota suhteessa virtaukseen f , määritettynä x f ( u ) = ∑ v ∈ V f ( v , u ) - ∑ v ∈ V f ( u , v ) ,
- c f : V × V → ℝ ∞ tarkoittavat jäännöskapasiteettifunktiota suhteessa virtaukseen f , määritelty arvolla c f ( e ) = c ( e ) - f ( e ) ,
- E f ⊂ E on reunat, joissa f < c ,
ja
- G f ( V , E f ) : llä jäljellä verkko on G nähden virtauksen f .
Push-nimetä uudelleen algoritmi käyttää positiivinen kokonaisluku voimassa merkintöjä toiminto , joka käyttää etäisyyden etikettien , tai korkeus , solmut mitkä kaaria tulisi valita push toimintaa. Tätä merkintätoimintoa merkitään 𝓁: V → ℕ . Tämän toiminnon on täytettävä seuraavat ehdot, jotta sitä voidaan pitää voimassa:
-
Voimassa oleva merkintä :
- 𝓁 ( u ) ≤ 𝓁 ( v ) + 1 kaikille ( u , v ) ∈ E f
-
Lähteen kunto :
- 𝓁 ( s ) = | V |
-
Tiskialtaan suojelu :
- 𝓁 ( t ) = 0
Algoritmissa s: n ja t: n etikettiarvot ovat kiinteät. 𝓁 ( u ) on painottamattoman etäisyyden alaraja u: sta t : ään G f: ssä, jos t on saavutettavissa u: sta . Jos u on irrotettu t: stä , niin 𝓁 ( u ) - | V | on painottamattoman etäisyyden alaraja u: sta s: ään . Tämän seurauksena, jos voimassa oleva merkintöjä toiminto on olemassa, ei ole olemassa s - t polkuja G f koska tällaisia polkuja voi olla pidempi kuin | V | - 1 .
Kaaren ( u , v ) ∈ E f kutsutaan käsiteltäväksi , jos 𝓁 ( u ) = 𝓁 ( v ) + 1 . Otettiin verkko G f ( V , E f ) koostuu joukon kaaria e ∈ E f , jotka ovat hyväksyttäviä. Sallittu verkko on asyklinen.
Toiminnot
Alustus
Algoritmi alkaa luomalla jäännösdiagrammi, alustamalla ennakkovirta-arvot nollaksi ja suorittamalla joukko kyllästäviä työntöoperaatioita lähteestä poistuville jäännöskaarille ( s , v ) missä v ∈ V \ { s } . Vastaavasti tarrat alustetaan siten, että lähteen tarra on kaavion solmujen määrä, 𝓁 ( s ) = | V | , ja kaikille muille solmuille annetaan nolla. Kun alustus on valmis, algoritmi suorittaa toistuvasti joko työntö- tai uudelleenmerkintäoperaatioita aktiivisia solmuja vastaan, kunnes mitään sovellettavaa toimintoa ei voida suorittaa.
Työntää
Push-toiminta koskee koskevasta tutkittavaksi out-kaari ( u , v ) aktiivisen solmun u on G f . Se siirtää min { x f ( u ), c f ( u , v )} virtausyksikköä u: sta v: ään .
push(u, v):
assert xf[u] > 0 and 𝓁[u] == 𝓁[v] + 1
Δ = min(xf[u], c[u][v] - f[u][v])
f[u][v] += Δ
f[v][u] -= Δ
xf[u] -= Δ
xf[v] += Δ
Työntöoperaatiota, joka saa f ( u , v ) saavuttamaan c ( u , v ), kutsutaan kyllästysponnistukseksi, koska se käyttää kaiken jäljellä olevan kaaren käytettävissä olevan kapasiteetin. Muussa tapauksessa kaikki solmun ylimäärä työnnetään jäännöskaaren yli. Tätä kutsutaan tyydyttymättömäksi tai tyydyttymättömäksi työntymiseksi .
Relabel
Uudelleentunnistustoiminto koskee aktiivista solmua u ilman sallittuja ulkokaaria G f: ssä . Se muuttaa 𝓁 ( u ) : n minimiarvoksi siten, että sallittu ulkoinen kaari luodaan. Huomaa, että tämä suurentaa aina 𝓁 ( u ) eikä koskaan luo jyrkää kaarta, joka on kaari ( u , v ) siten, että c f ( u , v )> 0 ja 𝓁 ( u )> 𝓁 ( v ) + 1 .
relabel(u):
assert xf[u] > 0 and 𝓁[u] <= 𝓁[v] for all v such that cf[u][v] > 0
𝓁[u] = 1 + min(𝓁[v] for all v such that cf[u][v] > 0)
Push and relabel -vaikutukset
Työntö- tai merkintäkäytön jälkeen 𝓁 pysyy kelvollisena merkintätoimintona f: n suhteen .
Push toiminnassa olevien tutkittavaksi kaari ( u , v ) , se voi lisätä kaari ( v , u ) ja E f , jossa 𝓁 ( v ) = 𝓁 ( u ) - 1 ≤ 𝓁 ( u ) + 1 ; se voi myös poistaa kaari ( u , v ) päässä E f , jossa se tehokkaasti poistaa rajoitus 𝓁 ( u ) ≤ 𝓁 ( v ) + 1 .
Nähdä, että nimetä uudelleen toimintansa solmuun u säilyttää pätevyyttä 𝓁 ( u ) , huomaa, että tämä on trivially takaama määritelmä out-kaaria U on G f . Käytössä olevien kaaria U in G f lisääntynyt 𝓁 ( u ) voi vain täyttää rajoitteet vähemmän tiukasti, eikä riko niitä.
Geneerinen push-relabel-algoritmi
Geneeristä push-relabel-algoritmia käytetään vain konseptin todisteena, eikä se sisällä toteutustietoja siitä, kuinka aktiivinen solmu valitaan push- ja relabel-operaatioille. Tämä algoritmin yleinen versio päättyy numeroon O ( V 2 E ) .
Koska 𝓁 ( s ) = | V | , 𝓁 ( t ) = 0 , eikä polkuja ole pidempi kuin | V | - 1 in G f , jotta 𝓁 ( t ) täyttää voimassa merkintöjä ehto t täytyy irrottaa t . Alustettaessa algoritmi täyttää tämän vaatimuksen luomalla esivirta f, joka kyllästää kaikki s: n ulokaaret , minkä jälkeen 𝓁 ( v ) = 0 on triviaalisesti voimassa kaikille v ∈ V \ { s , t } . Alustamisen jälkeen algoritmi suorittaa toistuvasti sovellettavan työntö- tai uudelleenmerkintäoperaation, kunnes sellaisia toimintoja ei sovelleta, jolloin esivirta on muunnettu maksimivirraksi.
generic-push-relabel(G, c, s, t):
create a pre-flow f that saturates all out-arcs of s
let 𝓁[s] = |V|
let 𝓁[v] = 0 for all v ∈ V \ {s}
while there is an applicable push or relabel operation do
execute the operation
Oikeus
Algoritmi ylläpitää ehtoa, että 𝓁 on kelvollinen merkintä suorituksen aikana. Tämä voidaan todistaa totta tutkimalla työntö- ja uudelleenmerkintöjen vaikutuksia tarratoimintoon 𝓁 . Relabel-toiminto lisää tarran arvoa siihen liittyvällä minimillä plus yhdellä, joka täyttää aina satisfy ( u ) ≤ 𝓁 ( v ) + 1 -rajoituksen. Työntötoiminto voi lähettää virtauksen u: sta v: ään, jos 𝓁 ( u ) = 𝓁 ( v ) + 1 . Tämä voi lisätä ( v , u ) ja G f ja voi poistaa ( u , v ) päässä G f . Lisäämällä ( v , u ) ja G f ei vaikuta voimassa merkintöjä alkaen 𝓁 ( v ) = 𝓁 ( u ) - 1 . Poistamista ( u , v ) päässä G f poistaa vastaava rajoite, koska voimassa merkintöjä ominaisuus 𝓁 ( u ) ≤ 𝓁 ( v ) + 1 koskee vain jäljellä kaarien G f .
Jos esivirtausaika f ja voimassa merkintöjä 𝓁 varten f on olemassa, ei ole mitään lisääviin polku s ja t on jäljellä kuvaaja G f . Tämä voidaan todistaa ristiriitaisuuteen, joka perustuu eriarvoisuuteen, joka syntyy leimausfunktiossa olettaen, että lisäyspolku on olemassa. Jos algoritmi päättyy, kaikki solmut ryhmässä V \ { s , t } eivät ole aktiivisia. Tämä tarkoittaa, että kaikilla v ∈ V \ { s , t } -laitteilla ei ole ylimääräistä virtausta, ja ilman ylimääräistä esivirtaus f noudattaa virtauksen säilymisrajoitusta ja sitä voidaan pitää normaalina virtauksena. Tämä virtaus on suurin virtaus maksimivirtauksen minileikatun lauseen mukaan, koska s: stä t: ään ei ole suurennusreittiä .
Siksi algoritmi palauttaa maksimivirran päätettäessä.
Ajan monimutkaisuus
Algoritmin aikakompleksisuuden sitomiseksi meidän on analysoitava pääpiirissä tapahtuvien push- ja relabel-operaatioiden määrä. Relabel-, saturating push- ja tyydyttymättömien push-operaatioiden lukumäärät analysoidaan erikseen.
Algoritmissa relabel-operaatio voidaan suorittaa enintään (2 | V | - 1) (| V | - 2) <2 | V | 2 kertaa. Tämä johtuu siitä, että minkä tahansa solmun u merkinnän 𝓁 ( u ) arvo ei voi koskaan laskea, ja suurin tarran arvo on korkeintaan 2 | V | - 1 kaikille solmuille. Tämä tarkoittaa, että uudelleenlähetystoiminto voidaan mahdollisesti suorittaa 2 | V | - 1 kertaa kaikille solmuille V \ { s , t } (ts. | V | - 2 ). Tämä johtaa sitoutuneen on O ( V 2 ) varten nimetä uudelleen käyttöön.
Jokainen kyllästetty painallus sallitussa kaaressa ( u , v ) poistaa kaaren G f: stä . Jotta kaari voidaan asettaa uudelleen G f : ään uudelle kyllästyspainallukselle, v on ensin merkittävä uudelleen, mitä seuraa kaaren painallus ( v , u ) , ja sitten u on merkittävä uudelleen. Prosessissa 𝓁 ( u ) kasvaa vähintään kahdella. Siksi on olemassa O ( V ) kyllästyspainikkeita ( u , v ) , ja tyydyttävien työntymisten kokonaismäärä on korkeintaan 2 | V || E | . Tämä johtaa O: n ( VE ) aikarajaan tyydyttäville työntötoiminnoille.
Tyydyttymättömien työntymisten määrän rajoittaminen voidaan saavuttaa mahdollisen argumentin avulla . Käytämme potentiaalifunktiota Φ = ∑ [ u ∈ V ∧ x f ( u )> 0] 𝓁 ( u ) (ts. Φ on kaikkien aktiivisten solmujen tarrojen summa). On selvää, että Φ on aluksi 0 ja pysyy negatiivisena koko algoritmin suorituksen ajan. Sekä uudelleennimitykset että tyydyttävät työntymät voivat lisätä Φ . Kuitenkin, arvo Φ on oltava yhtä suuri kuin 0 Lopettamisen koska ei voi olla mikä tahansa jäljellä oleva aktiivinen solmut lopussa algoritmin suorittamisen. Tämä tarkoittaa, että algoritmin suorittamisen aikana tyydyttymättömien työntöjen on korvattava uudelleennimityksen ja kyllästyspainotustoimintojen ero, jotta Φ päättyy arvolla 0. Relabelioperaatio voi lisätä Φ enintään (2 | V | - 1) (| V | - 2) . Kyllästävä painallus ( u , v ) aktivoi v: n, jos se ei ollut aktiivinen ennen työntöä, kasvoi Φ enintään 2 | V | - 1 . Näin ollen kokonaispanos kaikkien kyllästyslaskurin työntää toimintaansa Φ on korkeintaan (2 | V | - 1) (2 | V || E |) . Tyydyttymätön työntö ( u , v ) deaktivoi aina u: n , mutta se voi myös aktivoida v: n kuten tyydyttävässä työntämisessä. Tämän seurauksena se pienenee Φ vähintään 𝓁 ( u ) - 𝓁 ( v ) = 1 . Koska merkinnät ja tyydyttävät työntymät lisääntyvät Φ , tyydyttymättömien työntöjen kokonaismäärän on muodostettava ero (2 | V | - 1) (| V | - 2) + (2 | V | - 1) (2 | V || E |) ≤ 4 | V | 2 | E | . Tämä johtaa aikaan sidottuja on O ( V 2 E ) varten nonsaturating push toimintaa.
Yhteenvetona voidaan todeta, algoritmi suorittaa O ( V 2 ) relabels, O ( VE ) kyllästämällä työntää ja O ( V 2 E ) nonsaturating työntää. Tietorakenteet voidaan suunnitella valitsemaan ja suorittamaan sovellettava operaatio O (1) -ajalla. Näin ollen, kun algoritmin kompleksisuus on O ( V 2 E ) .
Esimerkki
Seuraava on esimerkki yllä määritellystä yleisestä push-relabel-algoritmista seuraavassa yksinkertaisessa verkon vuokaaviossa.
Esimerkissä h- ja e- arvot tarkoittavat solmun etikettiä 𝓁 ja liikaa x f , vastaavasti, algoritmin suorituksen aikana. Jokainen esimerkin jäännösgraafi sisältää vain jäännöskaaret, joiden kapasiteetti on suurempi kuin nolla. Kukin jäljellä kuvaaja voi sisältää useita toistojen suorittamiseen silmukka.
Esimerkki (mutta alkuvirta 0) voidaan suorittaa tässä vuorovaikutteisesti.
Käytännön toteutukset
Vaikka yleisellä push-relabel-algoritmilla on O ( V 2 E ) -aikakompleksisuus, tehokkailla toteutuksilla saavutetaan O ( V 3 ) tai matalampi aika-monimutkaisuus noudattamalla asianmukaisia sääntöjä sovellettavien push- ja relabel -operaatioiden valinnassa. Empiiristä suorituskykyä voidaan parantaa edelleen heuristiikalla.
"Nykykaaren" tietorakenne ja purkutoiminta
"Nykyinen valokaari" -datarakenne on mekanismi, jolla vierailee virtausverkon solmun sisään- ja ulospäin naapureissa staattisessa pyöreässä järjestyksessä. Jos solmulle luodaan vain linkitetty luettelo naapureista, tietorakenne voi olla yhtä yksinkertainen kuin osoitin luetteloon, joka kulkee luettelon läpi ja kelaa takaisin päähän loppuessaan.
"Nykyisen kaaren" tietorakenteen perusteella purkutoiminta voidaan määritellä. Vastuuvapausoperaatio koskee aktiivista solmua ja työntää toistuvasti virtausta solmusta, kunnes se muuttuu passiiviseksi, merkitsemällä se uudelleen tarpeen mukaan sallittujen kaarien luomiseksi prosessissa.
discharge(u):
while xf[u] > 0 do
if current-arc[u] has run off the end of neighbors[u] then
relabel(u)
rewind current-arc[u]
else
let (u, v) = current-arc[u]
if (u, v) is admissible then
push(u, v)
let current-arc[u] point to the next neighbor of u
Aktiivisen solmun valintasäännöt
Vastuuvapauden määrittely vähentää push-relabel-algoritmin aktiivisen solmun toistuvaksi valinnaksi purettavaksi. Valintasäännöstä riippuen algoritmilla on erilaisia aikakomplekseja. Lyhyyden vuoksi jätämme huomiotta s ja t, kun viitataan solmuihin seuraavassa keskustelussa.
FIFO-valintasääntö
FIFO push-nimetä uudelleen algoritmi järjestää aktiivinen solmut jonoon. Alkuperäiset aktiiviset solmut voidaan lisätä mielivaltaiseen järjestykseen. Algoritmi poistaa aina solun purkujonon edessä. Aina kun passiivinen solmu aktivoituu, se liitetään jonon takaosaan.
Algoritmi on O ( V 3 ) aikakompleksisuus.
Merkitse eteenpäin -valintasääntö
Relabel-to-front push-relabel -algoritmi järjestää kaikki solmut linkitettyyn luetteloon ja ylläpitää invarianttia, että luettelo on topologisesti lajiteltu sallitun verkon suhteen. Algoritmi skannaa luettelon edestä taakse ja suorittaa purkutoiminnon nykyiselle solmulle, jos se on aktiivinen. Jos solmu on merkitty uudelleen, se siirretään luettelon etuosaan ja skannaus aloitetaan uudelleen edestä.
Algoritmi on myös O ( V 3 ) aikakompleksisuus.
Korkein tarranvalintasääntö
Korkeimman otsikon push-relabel-algoritmi järjestää kaikki solmut tarroihinsa indeksoitaviin ryhmiin. Algoritmi valitsee aina aktiivisen solmun, jolla on suurin otsikko purettavaksi.
Algoritmilla on O ( V 2 √ E ) -aikakompleksisuus. Jos sen sijaan käytetään matalimman tarran valintasääntöä, ajan monimutkaisuudesta tulee O ( V 2 E ) .
Toteutustekniikat
Vaikka kuvauksessa yleinen push-nimetä uudelleen edellä mainittuun algoritmiin, 𝓁 ( u ) asetetaan nollaan kullekin solmulle u muu kuin s ja t alussa, se on edullista suorittaa taaksepäin leveys-ensimmäinen haku välillä t laskea tarkka tarrat.
Algoritmi on tyypillisesti jaettu kahteen vaiheeseen. Vaihe yksi laskee enintään ennalta virtauksen purkamiseksi vain aktiiviset solmut, joiden tunnisteet ovat alle n . Vaihe kaksi muuntaa suurimman esivirtauksen maksimivirraksi palauttamalla ylivirta, joka ei voi saavuttaa t: tä s: ksi . Voidaan osoittaa, että vaiheen kahdella on O ( VE ) -aikakompleksisuus riippumatta työntö- ja uudelleenlähetystoimintojen järjestyksestä, ja siksi sitä hallitsee ensimmäinen vaihe. Vaihtoehtoisesti se voidaan toteuttaa virtauksen hajotuksella.
Heuristiikka on ratkaisevan tärkeää algoritmin empiirisen suorituskyvyn parantamiseksi. Kaksi yleisesti käytettyä heuristiikkaa on aukkoheuristinen ja globaalin uudelleenmerkinnän heuristinen. Heuristinen aukko havaitsee aukot merkintöissä. Jos tunniste on 0 <𝓁 ' <| V | jolle ei ole solmua u , joka olisi 𝓁 ( u ) = 𝓁 ' , sitten mikä tahansa solmu u, jolla on 𝓁 ' <𝓁 ( u ) <| V | on irrotettu t: stä ja voidaan nimetä välittömästi (| V | + 1): een . Globaalin uudelleenmerkinnän heuristiikka suorittaa säännöllisesti taaksepäin suuntautuvan etsinnän t : stä G f : ssä solmujen tarkkojen etikettien laskemiseksi. Molemmat heuristiikat ohittavat hyödyttömiä uudelleenkäynnistystoimintoja, jotka ovat pullonkaula algoritmille ja lisäävät dynaamisten puiden tehottomuutta.
Esimerkkitoteutukset
#include <stdlib.h>
#include <stdio.h>
#define NODES 6
#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
#define INFINITE 10000000
void push(const int * const * C, int ** F, int *excess, int u, int v) {
int send = MIN(excess[u], C[u][v] - F[u][v]);
F[u][v] += send;
F[v][u] -= send;
excess[u] -= send;
excess[v] += send;
}
void relabel(const int * const * C, const int * const * F, int *height, int u) {
int v;
int min_height = INFINITE;
for (v = 0; v < NODES; v++) {
if (C[u][v] - F[u][v] > 0) {
min_height = MIN(min_height, height[v]);
height[u] = min_height + 1;
}
}
};
void discharge(const int * const * C, int ** F, int *excess, int *height, int *seen, int u) {
while (excess[u] > 0) {
if (seen[u] < NODES) {
int v = seen[u];
if ((C[u][v] - F[u][v] > 0) && (height[u] > height[v])) {
push(C, F, excess, u, v);
} else {
seen[u] += 1;
}
} else {
relabel(C, F, height, u);
seen[u] = 0;
}
}
}
void moveToFront(int i, int *A) {
int temp = A[i];
int n;
for (n = i; n > 0; n--) {
A[n] = A[n-1];
}
A[0] = temp;
}
int pushRelabel(const int * const * C, int ** F, int source, int sink) {
int *excess, *height, *list, *seen, i, p;
excess = (int *) calloc(NODES, sizeof(int));
height = (int *) calloc(NODES, sizeof(int));
seen = (int *) calloc(NODES, sizeof(int));
list = (int *) calloc((NODES-2), sizeof(int));
for (i = 0, p = 0; i < NODES; i++){
if ((i != source) && (i != sink)) {
list[p] = i;
p++;
}
}
height[source] = NODES;
excess[source] = INFINITE;
for (i = 0; i < NODES; i++)
push(C, F, excess, source, i);
p = 0;
while (p < NODES - 2) {
int u = list[p];
int old_height = height[u];
discharge(C, F, excess, height, seen, u);
if (height[u] > old_height) {
moveToFront(p, list);
p = 0;
} else {
p += 1;
}
}
int maxflow = 0;
for (i = 0; i < NODES; i++)
maxflow += F[source][i];
free(list);
free(seen);
free(height);
free(excess);
return maxflow;
}
void printMatrix(const int * const * M) {
int i, j;
for (i = 0; i < NODES; i++) {
for (j = 0; j < NODES; j++)
printf("%d\t",M[i][j]);
printf("\n");
}
}
int main(void) {
int **flow, **capacities, i;
flow = (int **) calloc(NODES, sizeof(int*));
capacities = (int **) calloc(NODES, sizeof(int*));
for (i = 0; i < NODES; i++) {
flow[i] = (int *) calloc(NODES, sizeof(int));
capacities[i] = (int *) calloc(NODES, sizeof(int));
}
// Sample graph
capacities[0][1] = 2;
capacities[0][2] = 9;
capacities[1][2] = 1;
capacities[1][3] = 0;
capacities[1][4] = 0;
capacities[2][4] = 7;
capacities[3][5] = 7;
capacities[4][5] = 4;
printf("Capacity:\n");
printMatrix(capacities);
printf("Max Flow:\n%d\n", pushRelabel(capacities, flow, 0, 5));
printf("Flows:\n");
printMatrix(flow);
return 0;
}
def relabel_to_front(C, source: int, sink: int) -> int:
n = len(C) # C is the capacity matrix
F = [[0] * n for _ in range(n)]
# residual capacity from u to v is C[u][v] - F[u][v]
height = [0] * n # height of node
excess = [0] * n # flow into node minus flow from node
seen = [0] * n # neighbours seen since last relabel
# node "queue"
nodelist = [i for i in range(n) if i != source and i != sink]
def push(u, v):
send = min(excess[u], C[u][v] - F[u][v])
F[u][v] += send
F[v][u] -= send
excess[u] -= send
excess[v] += send
def relabel(u):
# Find smallest new height making a push possible,
# if such a push is possible at all.
min_height = ∞
for v in xrange(n):
if C[u][v] - F[u][v] > 0:
min_height = min(min_height, height[v])
height[u] = min_height + 1
def discharge(u):
while excess[u] > 0:
if seen[u] < n: # check next neighbour
v = seen[u]
if C[u][v] - F[u][v] > 0 and height[u] > height[v]:
push(u, v)
else:
seen[u] += 1
else: # we have checked all neighbours. must relabel
relabel(u)
seen[u] = 0
height[source] = n # longest path from source to sink is less than n long
excess[source] = ∞ # send as much flow as possible to neighbours of source
for v in range(n):
push(source, v)
p = 0
while p < len(nodelist):
u = nodelist[p]
old_height = height[u]
discharge(u)
if height[u] > old_height:
nodelist.insert(0, nodelist.pop(p)) # move to front of list
p = 0 # start from front of list
else:
p += 1
return sum(F[source])