Minimalno razapinjuće stablo: Primov algoritam

Lekcija 13 od 17 · Grafovi i stabla

Primov algoritam gradi minimalno razapinjuće stablo tako što kreće od jednog čvora i u svakom koraku dodaje najjeftiniju granu koja povezuje stablo sa novim čvorom. Liči na Dijkstrin algoritam i takođe koristi priority_queue.

Pre nego što počneš: potrebno predznanje – Dijkstra, priority_queue.
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.

Šta je minimalno razapinjuće stablo?

Treba povezati sve gradove putevima tako da se iz svakog može stići u svaki, a ukupna cena puteva bude najmanja. Izabrane grane tada čine stablo (ciklus bi značio jedan suvišan put) koje pokriva sve čvorove – minimalno razapinjuće stablo (MST, engl. minimum spanning tree). Ima tačno n − 1 granu.

        4
   1 ------- 2
   |  \      |
 1 |   \ 7   | 2
   |    \    |
   3 ------- 4 ---- 5
        5       3

Grane: 1–2 (4), 1–3 (1), 1–4 (7), 2–4 (2), 3–4 (5), 4–5 (3).

MST ovog grafa: 1–3 (1), 2–4 (2), 4–5 (3), 1–2 (4) – ukupno 10.

Ideja Primovog algoritma

Delimo čvorove na one u stablu i one van njega. Na početku je u stablu samo čvor 1. U svakom koraku od svih grana koje vode iz stabla ka čvoru van stabla biramo najjeftiniju i dodajemo taj čvor. Ovaj pohlepni izbor je uvek ispravan: najjeftinija grana preko „granice“ stabla sigurno pripada nekom minimalnom stablu.

Prim iz čvora 1 (u koraku 2 grana 3–4 (5) je skuplja od 1–2 (4))
KorakDodata granaČvorovi u stabluUkupno
0–{1}0
11–3 (1){1, 3}1
21–2 (4){1, 2, 3}5
32–4 (2){1, 2, 3, 4}7
44–5 (3){1, 2, 3, 4, 5}10

Program

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

int main() {
    int n, m;
    cin >> n >> m;
    vector<vector<pair<int, int>>> graf(n + 1);
    for (int i = 0; i < m; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        graf[u].push_back({v, w});
        graf[v].push_back({u, w});
    }
    vector<bool> uStablu(n + 1, false);
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;   // (težina, čvor)
    pq.push({0, 1});
    long long ukupno = 0;
    int dodato = 0;
    while (!pq.empty()) {
        auto [w, v] = pq.top();
        pq.pop();
        if (uStablu[v]) continue;          // v je već povezan jeftinijom granom
        uStablu[v] = true;
        ukupno += w;
        dodato++;
        for (auto [u, c] : graf[v])
            if (!uStablu[u]) pq.push({c, u});
    }
    if (dodato < n) cout << "graf nije povezan" << endl;
    else cout << ukupno << endl;
    return 0;
}

Razlika u odnosu na Dijkstru: u red stavljamo težinu grane c, a ne rastojanje od starta dist[v] + c. Složenost O(m log m).

Prim ili Kruskal?

PrimKruskal
Idejaširi jedno stablo od čvorasortira grane i spaja delove (DSU)
Potrebnolista susedstva, priority_queuelista grana, sort, DSU
Gust graf (matrica)verzija O(n²) bez reda je najbržasporiji
Redak grafdobardobar, često kraći za pisanje

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

Optički kablovi

Treba povezati n zgrada optičkim kablovima. Dato je m mogućih kablova u v c (cena). Ispiši najmanju ukupnu cenu da sve zgrade budu povezane, ili NEMOGUCE.

Primer 1

Ulaz

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

Izlaz

6

Primer 2

Ulaz

4 2
1 2 1
3 4 1

Izlaz

NEMOGUCE
Česte greške i saveti
  • MST postoji samo za povezan graf – proveri da je dodato svih n čvorova.
  • U red se stavlja težina grane, ne zbir od starta (to bi bila Dijkstra).
  • Zbir težina čuvaj u long long.

Proveri znanje

Otvori kviz

Primov algoritam

1. Koliko grana ima minimalno razapinjuće stablo grafa sa n čvorova?




2. Koju granu Prim dodaje u svakom koraku?




3. Po čemu se Prim razlikuje od Dijkstre?