DP na DAG-u: broj puteva i najkraći put

Lekcija 10 od 17 · Grafovi i stabla

Kada graf nema ciklusa, čvorove možemo poređati topološki – tako da sve grane idu „unapred“. Tada se najkraći put, najduži put i broj puteva računaju jednim prolazom kroz čvorove, kao u dinamičkom programiranju: vrednost čvora zavisi samo od čvorova pre njega.

Pre nego što počneš: potrebno predznanje – topološko sortiranje, uvod u DP.
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.

Ideja

Ako su čvorovi u topološkom redosledu, onda su u trenutku obrade čvora v svi čvorovi iz kojih se stiže u v već obrađeni. Zato možemo „gurati“ vrednost iz v ka susedima:

ProblemPočetna vrednostPrelaz za granu v → u (težina w)
broj puteva od sbr[s] = 1, ostali 0br[u] += br[v]
najkraći put od sd[s] = 0, ostali ∞d[u] = min(d[u], d[v] + w)
najduži put od sd[s] = 0, ostali −∞d[u] = max(d[u], d[v] + w)

Za razliku od Dijkstre, ovo radi i sa negativnim težinama, a najduži put u grafu sa ciklusima je težak problem – u DAG-u je lak. Najduži put je detaljno obrađen u lekciji Najduži put u DAG-u.

Broj puteva

1 → 2 → 4
1 → 3 → 4
2 → 3
3 → 5
4 → 5
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

const long long MOD = 1000000007;

int main() {
    int n, m;
    cin >> n >> m;
    vector<vector<int>> graf(n + 1);
    vector<int> ulazni(n + 1, 0);
    for (int i = 0; i < m; i++) {
        int u, v;
        cin >> u >> v;
        graf[u].push_back(v);
        ulazni[v]++;
    }
    // topološki redosled (Kahn)
    vector<int> red;
    queue<int> q;
    for (int v = 1; v <= n; v++) if (ulazni[v] == 0) q.push(v);
    while (!q.empty()) {
        int v = q.front();
        q.pop();
        red.push_back(v);
        for (int u : graf[v]) if (--ulazni[u] == 0) q.push(u);
    }
    // DP: br[v] = broj puteva od 1 do v
    vector<long long> br(n + 1, 0);
    br[1] = 1;
    for (int v : red)
        for (int u : graf[v])
            br[u] = (br[u] + br[v]) % MOD;
    cout << br[n] << endl;
    return 0;
}

U drugom primeru od 1 do 5 ima 5 puteva: 1-3-5, 1-2-3-5, 1-3-4-5, 1-2-3-4-5, 1-2-4-5. Broj puteva raste eksponencijalno, zato se računa po modulu 109+7.

Najkraći put sa negativnim težinama

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

const long long INF = 1e18;

int main() {
    int n, m;
    cin >> n >> m;
    vector<vector<pair<int, int>>> graf(n + 1);
    vector<int> ulazni(n + 1, 0);
    for (int i = 0; i < m; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        graf[u].push_back({v, w});
        ulazni[v]++;
    }
    vector<int> red;
    queue<int> q;
    for (int v = 1; v <= n; v++) if (ulazni[v] == 0) q.push(v);
    while (!q.empty()) {
        int v = q.front();
        q.pop();
        red.push_back(v);
        for (auto [u, w] : graf[v]) if (--ulazni[u] == 0) q.push(u);
    }
    vector<long long> d(n + 1, INF);
    d[1] = 0;
    for (int v : red) {
        if (d[v] == INF) continue;              // v nije dostižan iz 1
        for (auto [u, w] : graf[v])
            if (d[v] + w < d[u]) d[u] = d[v] + w;
    }
    for (int v = 1; v <= n; v++) cout << (d[v] == INF ? string("INF") : to_string(d[v])) << (v < n ? " " : "\n");
    return 0;
}

Složenost je O(n + m) – brže i od Dijkstre.

Mreža je DAG

Klasičan zadatak „od gornjeg levog do donjeg desnog polja, krećući se samo desno i dole“ je DP na DAG-u: grane idu samo desno i dole, pa ciklusa nema, a redosled „red po red, sleva nadesno“ je topološki. Zato se takvi zadaci rešavaju dvostrukom petljom – vidi DP na mreži.

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

Putevi kroz mrežu sa preprekama

Data je mreža n × m sa poljima . (slobodno) i # (prepreka). Koliko ima puteva od gornjeg levog do donjeg desnog polja, ako se ide samo desno i dole? Odgovor po modulu 109+7.

Primer 1

Ulaz

3 3
...
.#.
...

Izlaz

2

Primer 2

Ulaz

2 2
.#
#.

Izlaz

0
ZADATAK 02Težina: Srednje

Najjeftinije putovanje

Grad ima n stanica i m jednosmernih linija u v c koje uvek vode ka stanici sa većim brojem (u < v), sa cenom c (neke linije imaju popust, pa je c negativno). Ispiši najmanju cenu puta od 1 do n ili NEMA.

Primer 1

Ulaz

4 5
1 2 4
1 3 5
2 3 -3
2 4 2
3 4 -1

Izlaz

0

Primer 2

Ulaz

3 1
1 2 5

Izlaz

NEMA
Česte greške i saveti
  • DP na DAG-u radi samo ako graf nema ciklus – proveri da je Kahn rasporedio svih n čvorova.
  • Ne relaksiraj iz čvorova koji nisu dostižni (vrednost ∞ ili −∞).
  • Broj puteva računaj po modulu i odmah ga primenjuj pri sabiranju.

Proveri znanje

Otvori kviz

DP na DAG-u

1. Zašto DP na DAG-u obrađuje čvorove u topološkom redosledu?




2. Da li DP na DAG-u radi sa negativnim težinama?




3. Kako se računa broj puteva do čvora u?