Cod sursa(job #2611007)

Utilizator alex.ivan1105Ivan Alexandru alex.ivan1105 Data 6 mai 2020 01:46:54
Problema Algoritmul Bellman-Ford Scor 100
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 5.9 kb
#include <bits/stdc++.h>
using namespace std;

/*
    Algoritmul lui Bellman-Ford:
        - se aplica pe graf orientat, conex, care are si muchii cu costuri negative

    -------------------------------------------------------------------------------

        Algoritmul determina daca graful are lant de cost negativ, caz in care afiseaza
    in fisier un mesaj. Daca nu, atunci el afiseaza costurile minime de la sursa la toate
    celelalte noduri din graf.
        Algoritmul basic are complexitate temporala de O(n * m), insa poate fi optimizat
    folosind o queue sau o priorityqueue, avand complexitate de O(n * m), respectiv
    O(n * m * log(n)), insa, in practica, ele se comporta mult mai ok.
        Implementarea de fata utilizeaza o coada de prioritate si are implementate si
    metode de afisare a drumurilor minime.
*/

#define NMAX 50007
#define INF (1 << 30)
#define NO_PARENT -1

class Task {
public:
    void solve() {
        read_input();
        get_result();
        print_solution();
    }

private:
    // numar de noduri
    int n;

    // numar de muchii
    int m;

    // vector de distante
    vector<int> dist;

    // vector de partinti folosit la reconstruirea path-ului
    vector<int> p;

    // liste de adiacenta
    vector<pair<int, int>> adj[NMAX];

    // sursa
    int source;

    // 
    bool foundNegativeCicle;

    void read_input() {
        ifstream fin("bellmanford.in");

        fin >> n >> m;

        dist.resize(n + 1);
        p.resize(n + 1);

        for (int i = 1; i <= m; ++i) {
            int x, y, cost;

            fin >> x >> y >> cost;

            adj[x].push_back({y, cost});

            // decomentezi daca vrei ca graful sa fie neorientat
            // adj[y].push_back({x, cost});
        }

        fin.close();
    }

    void get_result() {
        // modifici linia asta daca vrei alta sursa
        source = 1;

        // logica dijkstra din nodul source
        foundNegativeCicle = BellmanFord(source, dist, p);
    }

    // metoda intoarce 'true' daca a gasit ciclu de cost negativ; 'flase' altfel
    bool BellmanFord(int source, vector<int> &dist, vector<int> &p) {
        /*
            Min-heap ce va fi folosit pentru a stoca perechide tipul <distanta pana la sursa a nodului x, nodul x>.
            Este folosit pentru a extrage la fiecare pas nodul care are distanta cea mai mica pana la sursa.
        */
        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;

        for (int i = 1; i <= n; ++i) {
            dist[i] = INF;
            p[i] = NO_PARENT;
        }

        // Vector care retine de cate ori a fost folsit un nod (de cate ori a fost scos din coada).
        vector<int> used(n + 1, 0);

        // Distanta pana la sursa si parintele sunt prin conventie 0
        dist[source] = 0;
        p[source] = 0;

        pq.push({dist[source], source});

        while (!pq.empty()) {
            auto entry  = pq.top();
            pq.pop();

            int node = entry.second;
            int cost = entry.first;

            // cresc numarul de utilizare a nodului
            ++used[node];

            if (used[node] == n) {
                return true;
            }

            // daca nodul extras nu este updated
            if (cost > dist[node]) {
                continue;
            }

            for (auto &it : adj[node]) {
                int neighbour = it.first;
                int edge_cost = it.second;

                if (dist[node] + edge_cost < dist[neighbour]) {
                    // update la distanta
                    dist[neighbour] = dist[node] + edge_cost;
                    
                    // update la printe
                    p[neighbour] = node;

                    // adaug noua valoare pentru distanta pana la vecin in priority queue
                    pq.push({dist[neighbour], neighbour});
                }
            }
        }

        return false;
    }

    vector<int> rebuild_path(int node, vector<int> &p) {
        // daca nu a fost cuprins in parcurgere
        if (p[node] == NO_PARENT) {
            // returneaza vector gol
            return {};
        }

        // calea catre sursa
        vector<int> path;

        // adauga fiecare parinte in vectorul de cale
        for (; node != 0; node = p[node]) {
            path.push_back(node);
        }

        // path-ul e in ordine inversa pentru ca reconstruirea s-a facut pe
        // ierarhia de parinti, deci trebuie inversat
        reverse(path.begin(), path.end());
        
        return path;
    }

    void show_paths(vector<int> &p) {
        // parcurg fiecare nod
        for (int i = 1; i <= n; ++i) {
            // daca nodul actual este sursa
            if (source == i) {
                cout << i << ": nod sursa\n";
                continue;
            }

            // path-ul de la sursa la nodul actual
            vector<int> path = rebuild_path(i, p);

            // daca e vector gol nu s-a parcurs nodul
            if (path.size() == 0) {
                cout << i << ": nu s-a gasit path catre sursa\n";
                continue;
            }

            cout << i << ": ";

            for (auto &it : path) {
                cout << it << " ";
            }

            cout << endl;
        }
    }

    void print_solution() {
        ofstream fout("bellmanford.out");
        if (foundNegativeCicle) {
            fout << "Ciclu negativ!\n";
            return;
        } 

        for (int i = 1; i <= n; ++i) {
            if (i == source) {
                continue;
            }

            fout << dist[i] << " ";
        }

        // decomentez ce e mai jos daca vreau sa afisez si distantele minime

        // cout << endl;

        // show_paths(p);
        fout.close();
    }
};

int main() {
    // assert(freopen("bellmanford.in", "r", stdin) != NULL);
    // assert(freopen("bellmanford.out", "w", stdout) != NULL);

    Task *task = new Task();
    task->solve();
    delete task;

    return 0;
}