Bellman-Ford i Floyd-Warshall

Lekcija 12 od 17 · Grafovi i stabla

Dva algoritma za najkraće puteve koji rade i sa negativnim težinama: Bellman-Ford nalazi rastojanja od jednog čvora i otkriva negativne cikluse, a Floyd-Warshall u tri ugnježdene petlje računa rastojanja između svih parova čvorova.

Pre nego što počneš: potrebno predznanje – Dijkstra.
Kako da učiš: svaki primer prekucaj i pokreni, a zadatke na kraju prvo reši sam/a; uputstvo i rešenje otvori tek posle toga. Svi programi su u C++-u.

Bellman-Ford

Najkraći put bez ciklusa ima najviše n − 1 granu. Zato je dovoljno n − 1 puta proći kroz sve grane i relaksirati svaku: posle k-tog prolaza tačna su sva rastojanja do kojih se stiže sa najviše k grana.

Ako se i u n-tom prolazu neko rastojanje smanji, postoji negativan ciklus dostižan iz starta – njime se put može skraćivati beskonačno.

#include <iostream>
#include <vector>
using namespace std;

const long long INF = 1e18;

struct Grana { int u, v, w; };

int main() {
    int n, m;
    cin >> n >> m;
    vector<Grana> g(m);
    for (auto &e : g) cin >> e.u >> e.v >> e.w;   // usmerene grane u -> v
    vector<long long> dist(n + 1, INF);
    dist[1] = 0;
    for (int prolaz = 1; prolaz <= n - 1; prolaz++)
        for (auto &e : g)
            if (dist[e.u] != INF && dist[e.u] + e.w < dist[e.v])
                dist[e.v] = dist[e.u] + e.w;
    // n-ti prolaz: ako se još nešto skraćuje, postoji negativan ciklus
    for (auto &e : g)
        if (dist[e.u] != INF && dist[e.u] + e.w < dist[e.v]) {
            cout << "NEGATIVAN CIKLUS" << endl;
            return 0;
        }
    for (int v = 1; v <= n; v++) {
        if (dist[v] == INF) cout << "INF ";
        else cout << dist[v] << " ";
    }
    cout << endl;
    return 0;
}

Provera dist[e.u] != INF je važna: bez nje bi „∞ + negativna težina“ izgledalo kao skraćenje. Složenost je O(n · m) – sporije od Dijkstre, ali radi sa negativnim granama.

Floyd-Warshall

Matrica d[i][j] na početku sadrži težinu grane i → j (ili ∞, a d[i][i] = 0). Zatim za svaki čvor k proveravamo da li je put i → k → j kraći od dosadašnjeg i → j. Posle obrade čvora k matrica sadrži najkraće puteve koji kao međučvorove koriste samo čvorove 1..k.

#include <iostream>
#include <algorithm>
using namespace std;

const long long INF = 1e18;
long long d[401][401];

int main() {
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            d[i][j] = (i == j) ? 0 : INF;
    for (int i = 0; i < m; i++) {
        int u, v;
        long long w;
        cin >> u >> v >> w;
        d[u][v] = min(d[u][v], w);          // može biti više grana između istih čvorova
    }
    for (int k = 1; k <= n; k++)            // k mora biti SPOLJNA petlja
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                if (d[i][k] != INF && d[k][j] != INF)
                    d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (d[i][j] == INF) cout << "INF";
            else cout << d[i][j];
            cout << (j < n ? " " : "\n");
        }
    }
    return 0;
}

Složenost O(n³) – za n ≤ 400–500. Ako je posle algoritma neki d[i][i] < 0, čvor i je na negativnom ciklusu.

Koji algoritam izabrati?

AlgoritamOd kogaNegativne težineSloženostKada
BFSjedan čvor–O(n + m)sve grane iste dužine
Dijkstrajedan čvorneO((n + m) log n)najčešći slučaj
Bellman-Fordjedan čvorda, otkriva negativne cikluseO(n · m)negativne grane, n·m ≤ ~108
Floyd-Warshallsvi parovidaO(n³)mali graf, mnogo upita
DP na DAG-ujedan čvordaO(n + m)graf bez ciklusa

Zadaci za vežbu

Za svaki zadatak postoji primer, uputstvo i rešenje. Rešenja su provereno tačna na primerima iz teksta.

ZADATAK 01Težina: Srednje

Rastojanja između gradova

Dato je n ≤ 300 gradova i m dvosmernih puteva sa dužinama, zatim q upita a b. Za svaki upit ispiši dužinu najkraćeg puta, ili -1 ako put ne postoji.

Primer

Ulaz

4 3
1 2 5
2 3 2
1 3 9
3
1 3
3 1
1 4

Izlaz

7
7
-1
ZADATAK 02Težina: Teže

Zarada na menjačnici

Menjačnica ima n valuta i m kurseva oblika a b c: zamena valute a za b „košta“ c poena (c može biti negativno – tada se dobija). Da li postoji niz zamena koji počinje i završava se istom valutom, a ukupno donosi dobitak (zbir manji od 0)? Ispiši DA ili NE.

Primer 1

Ulaz

3 3
1 2 1
2 3 -2
3 1 0

Izlaz

DA

Primer 2

Ulaz

3 3
1 2 1
2 3 -1
3 1 1

Izlaz

NE
Česte greške i saveti
  • Kod Bellman-Forda ne relaksiraj grane iz čvora sa dist = ∞.
  • Kod Floyd-Warshalla petlja po k mora biti spoljašnja.
  • Za n = 1000 Floyd-Warshall je 109 operacija – prespor; tada pokreni Dijkstru iz svakog čvora ili čitaj zadatak ponovo.

Proveri znanje

Otvori kviz

Bellman-Ford i Floyd-Warshall

1. Koliko prolaza kroz sve grane je dovoljno Bellman-Fordu ako nema negativnog ciklusa?




2. Kako Bellman-Ford otkriva negativan ciklus?




3. Koja je složenost Floyd-Warshalla?