Algoritmi Najkraćih Puteva
U ovoj lekciji upoznaćemo 2 veoma korisna algoritma za najkraće puteve - Belman-Ford i Flojd-Voršal
Belman-Ford
Setimo se jednog jedinog ograničenja Dajkstrinog algoritma - težine ne smeju da budu negativne. Belman-Ford to rešava!

Pogledajmo prvo najkraći put između bilo koja dva čvora,
On može da ima najviše n-1 granu! (Kada bi ih imao više, u njemu bi se našao nepotreban ciklus)
To znači da ćemo najkraći put sigurno naći ako sve čvorove uporedimo n-1 put!
A kako ih poredimo?
Poređenje se svodi na proveru da li do nekog čvora možemo brže da stignemo iz nekog drugog: if(dist[from] + w < dist[to]) -> popravljamo rastojanje
Ostaje još jedan slučaj o kojem moramo da vodimo računa, a to je kada najkraći put uopšte ne postoji: ciklusi negativne težine

Ovde najkraćeg puta nema - odlazak od A do B pa do C svaki put smanji dužinu za 3, a to možemo da ponavljamo beskonačno i tako dobijemo dužinu .
Belman-Ford ume to da prepozna - ako se rešenje ne nađe u n-1 koraku, graf ima ciklus negativne težine
Kod:
Bukvalno samo 2 for petlje:
//pretpostavka je da je adj[i] oblika {from, to, weight}, a kod se lako prilagodi bilo kom drugom zapisu, ovaj je obično najjednostavniji
vector<int> bellmanFord(int n, vector<vector<int>>& adj) {
vector<int> dist(n, 1e8);
dist[0] = 0;
for (int i = 0; i < n; i++) {
for (vector<int> edge : adj) {
int from = edge[0];
int to = edge[1];
int w = edge[2];
if (dist[from] != 1e8 && dist[from] + w < dist[to]) {
if(i == n - 1)
return {-1}; // ciklus negativne težine
dist[to] = dist[from] + w;
}
}
}
return dist;
}Vremenska složenost: O( V * E ) - gde je V broj čvorova, a E broj grana
Flojd-Voršal
Još jedan veoma koristan algoritam je Flojd-Voršal, koji nam u O() daje najkraće puteve između svih parova čvorova.

Kao i kod Dajkstre, težine ne smeju da budu negativne!
Ideja je brute force koliko god može da bude, a kreće od dvodimenzione matrice suseda:

Ako od bilo kog čvora A do bilo kog drugog čvora B prolazimo kroz čvor C, onda, ako je AB optimalno, i AC i CB moraju da budu optimalni!
Hajde sada da svaki čvor redom uzmemo kao C, to jest da vidimo koliko drugih čvorova tekući čvor povezuje. (Radićemo to redom od 0 do n, čime nam je zagarantovano da je tekuće C u tom trenutku najbolje što može da bude)
Sada je dovoljna trostruka for petlja: za svaki mogući međučvor C probamo svaki čvor kao A, a za svaki od njih probamo svaki čvor kao B, pa ako nešto može da se popravi - popravimo ga.
Kada stignemo do C == n, zadatak je rešen.



Kod:
void floydWarshall(vector<vector<int>> &dist, int n) {
int INF = 1e8;
// za svaki međučvor
for (int c = 0; c < n; c++) {
// redom uzimamo svaki čvor kao početni
for (int a = 0; a < n; a++) {
// pa za taj početni redom uzimamo svaki čvor kao krajnji
for (int b = 0; b < n; b++) {
// najkraći put od a do b
if(dist[a][c] != INF && dist[c][b]!= INF ){
dist[a][b] = min(dist[a][b], dist[a][c] + dist[c][b]);
}
}
}
}
}Vremenska složenost: O()
Isto bismo mogli da postignemo i tako što bismo Dajkstru pustili iz svakog čvora.
Flojd-Voršal je bolji kada je graf gust (ima mnogo grana), a Dajkstra iz svakog čvora kada je graf redak. Ipak, zbog toga što se tako jednostavno piše, Flojd-Voršal se daleko češće koristi!