Eulerovi putevi i ciklusi

Lekcija 15 od 17 · Grafovi i stabla

Može li se nacrtati figura jednim potezom, bez podizanja olovke i bez prelaženja iste linije dva puta? To je pitanje da li graf ima Eulerov put – put koji prolazi kroz svaku granu tačno jednom. Odgovor zavisi samo od stepena čvorova, a sam put nalazi Hierholzerov algoritam.

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

Kada postoji?

Za neusmeren graf u kome su sve grane u jednoj komponenti:

Broj čvorova neparnog stepenaRezultat
0postoji Eulerov ciklus (put se vraća u početni čvor) – kreće se iz bilo kog čvora
2postoji Eulerov put – počinje u jednom, a završava se u drugom čvoru neparnog stepena
više od 2ne postoji

Zašto: svaki put kada prođemo kroz čvor, koristimo jednu ulaznu i jednu izlaznu granu, pa unutrašnji čvorovi puta moraju imati paran stepen. Samo početak i kraj mogu biti neparni.

Za usmeren graf: ciklus postoji ako je za svaki čvor broj ulaznih grana jednak broju izlaznih; put postoji ako jedan čvor ima jednu izlaznu granu više (početak), jedan jednu ulaznu više (kraj), a svi ostali su uravnoteženi.

Čuveni problem sedam mostova Königsberga (Kenigsberga) nema rešenje jer sva četiri dela grada imaju neparan stepen.

Hierholzerov algoritam

Krećemo iz početnog čvora i idemo neiskorišćenim granama dok ne zaglavimo. Kada iz čvora više nema neiskorišćenih grana, on ide u rezultat i vraćamo se korak nazad – odatle eventualno krećemo u novi „krug“ koji se umeće u put. Rezultat se dobija obrnutim redom. Svaka grana se koristi jednom, pa je složenost O(n + m).

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

int main() {
    int n, m;
    cin >> n >> m;
    vector<vector<pair<int, int>>> graf(n + 1);   // (sused, redni broj grane)
    vector<int> stepen(n + 1, 0);
    for (int i = 0; i < m; i++) {
        int u, v;
        cin >> u >> v;
        graf[u].push_back({v, i});
        graf[v].push_back({u, i});
        stepen[u]++;
        stepen[v]++;
    }
    // početak: čvor neparnog stepena, ako postoji; inače bilo koji čvor sa granom
    int start = 1, neparnih = 0;
    for (int v = 1; v <= n; v++) if (stepen[v] > 0) { start = v; break; }
    for (int v = 1; v <= n; v++)
        if (stepen[v] % 2 == 1) {
            if (neparnih == 0) start = v;
            neparnih++;
        }
    if (neparnih != 0 && neparnih != 2) {
        cout << "NE POSTOJI" << endl;
        return 0;
    }
    vector<bool> iskoriscena(m, false);
    vector<size_t> sledeca(n + 1, 0);           // od koje grane dalje gledamo listu čvora
    vector<int> stek = {start}, put;
    while (!stek.empty()) {
        int v = stek.back();
        while (sledeca[v] < graf[v].size() && iskoriscena[graf[v][sledeca[v]].second]) sledeca[v]++;
        if (sledeca[v] == graf[v].size()) {     // nema više grana iz v
            put.push_back(v);
            stek.pop_back();
        } else {
            auto [u, id] = graf[v][sledeca[v]];
            iskoriscena[id] = true;             // prelazimo granu v-u
            stek.push_back(u);
        }
    }
    if ((int)put.size() != m + 1) {             // nisu sve grane u istoj komponenti
        cout << "NE POSTOJI" << endl;
        return 0;
    }
    reverse(put.begin(), put.end());
    for (size_t i = 0; i < put.size(); i++) cout << put[i] << (i + 1 < put.size() ? " " : "\n");
    return 0;
}

Algoritam je napisan sa sopstvenim stekom umesto rekurzije, jer put može imati milion grana. Niz sledeca čuva dokle smo stigli u listi suseda, da se iste (već iskorišćene) grane ne bi proveravale iznova.

Napomena: u starijoj verziji ove lekcije bio je opisan Fleuryjev algoritam. On je lakši za razumevanje, ali pri svakom koraku proverava da li je grana most, pa radi O(m²) – za takmičenja je prespor.

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: Lako

Jednim potezom

Figura je zadata kao neusmeren povezan graf (tačke i linije). Ispiši CIKLUS ako se može nacrtati jednim potezom tako da se završi u početnoj tački, PUT ako može, ali ne u početnoj tački, i NE ako ne može.

Primer 1

Ulaz

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

Izlaz

PUT

Primer 2

Ulaz

3 3
1 2
2 3
3 1

Izlaz

CIKLUS

Primer 3

Ulaz

4 3
1 2
1 3
1 4

Izlaz

NE
Česte greške i saveti
  • Uslovi o parnosti važe samo ako su sve grane u jednoj komponenti – proveri da put sadrži svih m grana.
  • Svaku granu označi kao iskorišćenu po rednom broju, da se ne bi prešla dva puta (jednom iz svakog kraja).
  • Za veliki graf koristi verziju sa stekom, ne rekurziju.

Proveri znanje

Otvori kviz

Eulerovi putevi

1. Kada neusmeren povezan graf ima Eulerov ciklus?




2. Graf ima 4 čvora neparnog stepena. Da li ima Eulerov put?




3. Koja je složenost Hierholzerovog algoritma?