Cod sursa(job #2819189)

Utilizator anaop32Oprea Ana-Maria anaop32 Data 18 decembrie 2021 02:49:21
Problema Ciclu hamiltonian de cost minim Scor 20
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 20.39 kb
#include <bits/stdc++.h>
using namespace std;

// ifstream f("dfs.in");
// ofstream g("dfs.out");

// ifstream f("bfs.in");
// ofstream g("bfs.out");

// ifstream f("ctc.in");
// ofstream g("ctc.out");

// ifstream f("sortaret.in");
// ofstream g("sortaret.out");

// ifstream f("hh.in");
// ofstream g("hh.out");

// ifstream f("apm.in");
// ofstream g("apm.out");

// ifstream f("dijkstra.in");
// ofstream g("dijkstra.out");

// ifstream f("ciclueuler.in");
// ofstream g("ciclueuler.out");

ifstream f("hamilton.in");
ofstream g("hamilton.out");

class Graf{
private:
    int nrNoduri;
    vector<vector<int>> listaVecini;
    vector<vector<pair<int,int>>> listaVeciniCost;

    //setteri
    void set_nrNoduri(int);
    void set_listaVecini();

    void adaugaMuchie(int, int);

    //metode
    void print_listaVecini() const;
    //citire graf orientat
    void citireOrientat(int, int);
    //citire graf neorientat
    void citereNeorientat(int, int);
    //citire graf neorientat cu costuri
    void citireNeorientatCosturi(int, int);
    //citire graf orientat cu costuri
    void citireOrientatCosturi(int, int);
    void DFS(int, vector<int>&);
    void BFS(int, vector<int>&);
    void DFS_CTC(int, vector<int>&, vector<int>&, stack<int>&, vector<bool>&, int&);
    vector<int> APM();

public:
    Graf();
    Graf(int);
    // mai adauga un constructor
    void adaugaMuchieCost(int, int, int);
    void set_listaVeciniCosturi();
    void citireEul();
    //getteri
    int get_nrNoduri() const;

    // DFS
    int nrCompConexe();

    // BFS
    void Distante();

    // CTC
    set<int> Tarjan();

    // Sortare topologica
    vector<int> Kahn();

    // APM
    void printAPM();

    // Dijkstra
    void Dijkstra();

    // ciclu eulerian
    void hasEulerCircuit();
    void EulerCircuit(int);
    bool isBridge(int, int);
    int getConnected(int nod, vector<bool> &visited);

    // hamilton
    // bool hasHamiltonCycle();
    // bool bktHamilton(vector<int>, int);
    // bool tryVertexHamilton(vector<int>, int, int);

    // hamilton
    int hamilton(vector<vector<int>>);
};

void Graf::adaugaMuchieCost(int nod1, int nod2, int cost){
    listaVeciniCost[nod1 - 1].push_back(make_pair(nod2 - 1, cost));
}

int Graf::hamilton(vector<vector<int>> mat){
    vector<int> vertices;
    for (int i = 1; i < get_nrNoduri(); i++){
        vertices.push_back(i);
    }

    int minimum = INT_MAX;
    do{
        int visited = 1;
        int curr_weight = 0;
        int vertex = 0;

        for (int i = 0; i < vertices.size(); i++){
            if (mat[vertex][vertices[i]] != 0){
                visited ++;
                curr_weight += mat[vertex][vertices[i]];
                vertex = vertices[i];
            }
        }
        if (mat[vertex][0] != 0){
            curr_weight += mat[vertex][0];
            if (visited == get_nrNoduri())
                minimum = min(minimum, curr_weight);
        }

    } while (next_permutation(vertices.begin(), vertices.end()));

    return minimum;
}

// bool Graf::hasHamiltonCycle(){
//     int n, m;
//     f >> n >> m;
//     citireOrientatCosturi(n,m);
//     vector<int> path(get_nrNoduri(), -1);

//     path[0] = 0;
//     return bktHamilton(path, 1);
// }

// bool Graf::bktHamilton(vector<int> path, int nrVerticesPath){

//     // if (nrVerticesPath == get_nrNoduri()){
//     //     for (int i = 0; i < get_nrNoduri(); i++){
//     //         if (listaVecini[path[nrVerticesPath - 1]][i] == 1){
//     //             return true;
//     //         }
//     //     }
//     //     return false;
//     // }

//     // for (int i = 1; i < get_nrNoduri(); i++){
//     //     if (tryVertexHamilton(path, i, nrVerticesPath) == true){
//     //         path[nrVerticesPath] = i;

//     //         if (bktHamilton(path, nrVerticesPath + 1) == true)
//     //             return true;

//     //         path[nrVerticesPath] = -1;
//     //     }
//     // }
//     // return false;

//     if (nrVerticesPath == get_nrNoduri()){
//         if (listaVeciniCost[nrVerticesPath - 1][path[0]] != 0){
//             return true;
//         }
//         return false;
//     }
//     for (int i = 1; i < get_nrNoduri(); i++){
//         if (tryVertexHamilton(path, i, nrVerticesPath) == true){
//             path[nrVerticesPath] = i;

//             if (bktHamilton(path, nrVerticesPath + 1) == true)
//                 return true;

//             path[nrVerticesPath] = -1;
//         }
//     }
//     return false;
// }

// bool Graf::tryVertexHamilton(vector<int> path, int vertex, int nrVerticesPath){
//     // int ok = 0;
//     // for (int i = 0; i < listaVecini.size(); i++){
//     //     if (listaVecini[nrVerticesPath - 1][i] == vertex)
//     //         ok = 1;
//     // }
//     // if (ok == 0)
//     //     return false;

//     // for (int i = 0; i < nrVerticesPath; i++){
//     //     if (path[i] == vertex)
//     //         return false;
//     // }
//     // return true;

//     if (listaVeciniCost[path[nrVerticesPath - 1]][vertex] == 0)
//         return false;
//     for (int i = 0; i < nrVerticesPath; i++){
//         if (path[i] == vertex){
//             return false;
//         }
//     }
//     return true;
// }

void Graf::hasEulerCircuit(){
    int n, m;
    f >> n >> m;
    citereNeorientat(n ,m);

    int odd = 0;
    for (int i = 0; i < get_nrNoduri(); i++){
        if (listaVecini[i].size() % 2 != 0)
            odd ++;
    }
    if (odd == 0)
        EulerCircuit(0);
    else
        g << -1;
}

void Graf::EulerCircuit(int nod){
    g << nod + 1 << " ";

    if (listaVecini[nod].size() == 0){
        return;
    }

    if (listaVecini[nod].size() == 1){
        int c = listaVecini[nod][0];
        swap(listaVecini[nod][0], listaVecini[nod][listaVecini[nod].size() - 1]);
        listaVecini[nod].pop_back();
        for (int j = 0; j < listaVecini[c].size(); j++){
            if (listaVecini[c][j] == nod){
                swap(listaVecini[c][j], listaVecini[c][listaVecini[c].size() - 1]);
                listaVecini[c].pop_back();
                break;
            }
        }
        EulerCircuit(c);
        return;
    }

    for (int i = 0; i < listaVecini[nod].size(); i++){
        if (isBridge(nod, listaVecini[nod][i]) == false){
            int c = listaVecini[nod][i];

            swap(listaVecini[nod][i], listaVecini[nod][listaVecini[nod].size() - 1]);
            listaVecini[nod].pop_back();

            for (int j = 0; j < listaVecini[c].size(); j++){
                if (listaVecini[c][j] == nod){
                    swap(listaVecini[c][j], listaVecini[c][listaVecini[c].size() - 1]);
                    listaVecini[c].pop_back();
                    break;
                }
            }
            EulerCircuit(c);
            return;
        }
    }
}

bool Graf::isBridge(int nod1, int nod2){
    int nrMuchii1;
    int nrMuchii2;
    vector<vector<int>> grafCopy = listaVecini;
    vector<int> rowCopy1 = listaVecini[nod1];
    vector<int> rowCopy2 = listaVecini[nod2];
    vector<bool> visited;

    for (int i = 0; i < listaVecini[nod1].size(); i++){
        if (listaVecini[nod1][i] == nod2){
            swap(listaVecini[nod1][i], listaVecini[nod1][listaVecini[nod1].size() -1]);
            listaVecini[nod1].pop_back();
            break;
        }
    }

    for (int i = 0; i < listaVecini[nod2].size(); i++){
        if (listaVecini[nod2][i] == nod1){
            swap(listaVecini[nod2][i], listaVecini[nod2][listaVecini[nod2].size() -1]);
            listaVecini[nod2].pop_back();
            break;
        }
    }

    visited = vector<bool>(get_nrNoduri(), false);

    nrMuchii1 = getConnected(nod1, visited);
    listaVecini[nod1] = rowCopy1;
    listaVecini[nod2] = rowCopy2;

    visited = vector<bool>(get_nrNoduri(), false);
    nrMuchii2 = getConnected(nod2, visited);

    if (nrMuchii1 == nrMuchii2)
        return false;
    else
        return true;
}


int Graf::getConnected(int nod, vector<bool> &visited){
    visited[nod] = true;
    int count = 1;

    for (int i = 0; i < listaVecini[nod].size(); i++){
        if (visited[listaVecini[nod][i]] == false)
            count += getConnected(listaVecini[nod][i], visited);
    }
    return count;
}

Graf::Graf(int n){
    this->nrNoduri = n;
    for (int i = 1; i <= nrNoduri; i++){
        vector<int> aux;
        listaVecini.push_back(aux);
    }
}

Graf::Graf(){

}

void Graf::adaugaMuchie(int m1, int m2){
    listaVecini[m1].push_back(m2);
}

int Graf::get_nrNoduri() const{
    return this->nrNoduri;
}

void Graf::set_nrNoduri(int n){
    this->nrNoduri = n;
}

void Graf::set_listaVecini(){
    for (int i = 0; i < this->get_nrNoduri(); i++){
        vector<int> aux;
        listaVecini.push_back(aux);
    }
}

void Graf::set_listaVeciniCosturi(){
    listaVeciniCost.resize(get_nrNoduri() + 1);
    //for (int i = 0; i < this->get_nrNoduri(); i++){
       // vector<int> aux(get_nrNoduri(), 0);
       // listaVeciniCost.push_back(aux);
   // }
}

void Graf::print_listaVecini() const{
    for (int i = 0; i < this->get_nrNoduri(); i++){
        cout << i + 1 << ": ";
        for (int j = 0; j < listaVecini[i].size(); j++){
            cout << listaVecini[i][j] + 1 << " ";
        }
        cout << "\n";
    }
}

void Graf::citireOrientat(int n, int m){
    int nod1,
        nod2;

    this->set_nrNoduri(n);
    this->set_listaVecini();
    for (int i = 1; i <= m; i++){
        f >> nod1 >> nod2;
        this->adaugaMuchie(nod1 - 1, nod2 - 1);
    }
}

void Graf::citereNeorientat(int n, int m){
    int nod1, // primul nod al muchiei
        nod2; // al doilea nod al muchiei

    this->set_nrNoduri(n);
    this->set_listaVecini();

    for (int i = 0; i < m; i++){
        f >> nod1;
        f >> nod2;
        this->adaugaMuchie(nod1 - 1, nod2 - 1);
        this->adaugaMuchie(nod2 - 1, nod1 - 1);
    }
}

void Graf::citireNeorientatCosturi(int n, int m){
    int nod1, // primul nod al muchiei
        nod2,
        cost; // al doilea nod al muchiei

    this->set_nrNoduri(n);
    this->set_listaVeciniCosturi();

    for (int i = 0; i < m; i++){
        f >> nod1;
        f >> nod2;
        f >> cost;
        //this->listaVeciniCost[nod1 - 1][nod2 - 1] = cost;
        //this->listaVeciniCost[nod2 - 1][nod1 - 1] = cost;
    }
}

void Graf::citireOrientatCosturi(int n, int m){
    int nod1, // primul nod al muchiei
        nod2,
        cost; // al doilea nod al muchiei

    this->set_nrNoduri(n);

    this->set_listaVeciniCosturi();


    for (int i = 0; i < m; i++){
        f >> nod1;
        f >> nod2;
        f >> cost;
        this->listaVeciniCost[nod1 - 1].push_back({nod2 - 1, cost});
    }
}

void Graf::DFS(int nod, vector<int> &vizitat){
    vizitat[nod] = 1; // vizitez nodul curent

    // pentru fiecare vecin al nodului curent
    for (auto i : listaVecini[nod]){
        if (vizitat[i] == 0){ // daca nu e vizitat atunci facem dfs pe el (avem i-1 pentru ca indicii pornesc de la 0 dar nodul meu e numerotat de la 1)
            DFS(i, vizitat);
        }
    }
}

int Graf::nrCompConexe(){
    int n,
        m;
    f >> n >> m;
    this->citereNeorientat(n, m);

    vector<int> vizitat(this->get_nrNoduri(), 0); // vector care retine daca un nod a fost vizitat sau nu
    int nrComponenteConexe = 0;

    for(int i = 0; i < this->get_nrNoduri(); i++){
        if (vizitat[i] == 0){
            nrComponenteConexe ++;
            DFS(i, vizitat);
        }
    }

    return nrComponenteConexe;
}

void Graf::BFS(int s, vector<int> &distanta){
    vector<int> vizitat (this->get_nrNoduri(), 0); // vector care retine care retin nodurile vizitate
    queue<int> coada;   // coada pentru bfs in care retin nodurile care urmeaza sa le parcurg

    vizitat[s - 1] = 1; // pornesc de la nodul de start dat in cerinta
    coada.push(s);  // adaug in coada nodul meu de start

    // cat timp coada nu e goala
    while(coada.empty() == false){
        int nod = coada.front(); // in nod retin nodul curent pe care il parcurg (il scot din coada)
        coada.pop(); // il scot din coada pentru ca l-am parcurs deja

        // adaug in coada fiecare vecin al nodului meu care nu a fost inca vizitat
        for (int i = 0 ; i < listaVecini[nod - 1].size(); i++){
            if (vizitat[listaVecini[nod - 1][i] - 1] == 0){ // daca nu a fost vizitat
                vizitat[listaVecini[nod - 1][i] - 1] = 1;   // il marchez ca fiind vizitat
                coada.push(listaVecini[nod - 1][i]);    // il adaug in coada
                distanta[listaVecini[nod - 1][i] - 1] = distanta[nod - 1] + 1;  // ii adaug distanta in vectorul de distante
            }
        }
    }

}

void Graf::Distante(){
    int n,
        m,
        s;

    f >> n >> m >> s;

    this->citireOrientat(n,m);

    vector<int> distanta (this->get_nrNoduri() , 0);

    BFS(s, distanta);

    for (int i = 0 ; i < this->get_nrNoduri(); i++){
        // daca distanta este 0 si nodul respectiv nu este nodul de start atunci inseamna ca nu putem sa ajungem la nodul respectiv deci distanta de la s la el este - 1
        if (distanta[i] == 0 && i + 1 != s){
            distanta[i] = -1;
        }
        g << distanta[i] << " ";
   }
}

void Graf::DFS_CTC(int nod_curent, vector<int> &descoperit, vector<int> &low, stack<int> &stiva, vector<bool> &membruStiva, int &nr){
    static int timp = 0;
    descoperit[nod_curent] = low[nod_curent] = timp++;
    stiva.push(nod_curent);
    membruStiva[nod_curent] = true;

    // pentru fiecare vecin al nodului curent verificam daca a fost vezitat, daca nu a fost vizitat facem dfs pe el
    for (auto elem: listaVecini[nod_curent]){
        if (descoperit[elem] == -1){
            DFS_CTC(elem, descoperit, low, stiva, membruStiva, nr);
        }
        // daca elementul nostru se afla te stiva atunci modificam valoarea low a nodului in minimul dintre low nodului curent si low vecinului curent
        if(membruStiva[elem] == true){
            low[nod_curent] = min(low[nod_curent], low[elem]);
        }
    }
    // daca avem ca timpul lui de descoperire este identic cu low atunci am gasit o componeneta tare conexa
    if (descoperit[nod_curent] == low[nod_curent]){
        // parcurgem stiva pana dam de nodul nostru curent
        // adica fiecare nod pe care l-am descoperit dupa nodul curent face parte din componenta noastra tare conexa
        for (int nod = stiva.top();;nod = stiva.top()){
            stiva.pop();
            membruStiva[nod] = false;
            low[nod] = descoperit[nod_curent];
            if (nod == nod_curent)
                break;
        }
        nr++;
    }
}

set<int> Graf::Tarjan(){
    int n,
        m;
    f >> n >> m;
    this->citireOrientat(n, m);
    vector<int> descoperit(this->get_nrNoduri(), -1);
    vector<int> low(this->get_nrNoduri(), -1);
    stack<int> stiva;
    vector<bool> membruStiva(this->get_nrNoduri(), false);
    int nr = 0; // numarul componenetelor tare conexe

    for (int i = 0; i < this->get_nrNoduri(); i++){
        if (descoperit[i] == -1)
            DFS_CTC(i, descoperit, low, stiva, membruStiva, nr);
    }
    g << nr << "\n";
    set<int> multime;
    for (int i = 0; i < this->get_nrNoduri(); i++){
        multime.insert(low[i]);
    }

    return multime;
    // for (auto elem : multime){
    //     for (int i = 0; i < this->get_nrNoduri(); i++){
    //         if (low[i] == elem)
    //            g << i + 1<< " ";
    //     }
    //     g << "\n";
    // }
}

vector<int> Graf::Kahn(){
    int n,
        m;
    f >> n >> m;
    this->citireOrientat(n, m);
    vector<int> in_degree(this->get_nrNoduri(),0);
    queue<int> q;
    vector<int> order;
    int nod;

    for (int i = 0; i < this->get_nrNoduri(); i++){
        for (auto elem: listaVecini[i]){
            in_degree[elem]++;
        }
    }

    for (int i = 0; i < this->get_nrNoduri(); i++){
        if (in_degree[i] == 0)
            q.push(i);
    }

    while(q.empty() == 0){
        nod = q.front();
        q.pop();
        order.push_back(nod);
        for (auto elem: listaVecini[nod]){
            in_degree[elem]--;
            if (in_degree[elem] == 0)
                q.push(elem);
        }
    }

    return order;
    // for (int i = 0; i < this->get_nrNoduri(); i++){
    //     g << order[i] + 1<< " ";
    // }
}

bool HavelHakimi(){
    // citere date de intrare
    int n, x;
    f >> n;
    vector<int> grad;

    for (int i = 0; i < n; i++){
        f >> x;
        grad.push_back(x);
    }

    while(true){

        // sortam elementele crescator
        sort(grad.begin(), grad.end(), greater<int>());

        int count = 0;

        // verificam daca toate elementele sunt egale cu 0
        for (auto elem: grad)
            if (elem != 0)
                count++;


        // daca toate sunt 0 atunci insemana ca se paote construi un graf
        if (count == 0)
            return true;


        int poz = grad.size();

        // caut pozitia primului 0 si il retin in poz
        for (int i = 0; i < grad.size(); i++){
            if (grad[i] == 0){
                poz = i;
                break;
                }
        }

        // daca gradul curent e mai mare decat numarul de noduri disponibile atunci nu putem sa construim graful
        if (grad[0] > poz - 1)
            return false;

        for (int i = 1; i < poz; i++){
            grad[i]--; // scad gradul nodului la care pot sa duc o muchie
            grad[0]--; // scad si gradul nodului din care duc muchii
            if (grad[i] < 0) // daca dau de o valoare negativa inseamna ca nu pot sa fac un graf
                return false;
        }

} }

vector<int> Graf::APM(){
    int n,
        m;
    f >> n >> m;
    this->citireNeorientatCosturi(n ,m);
    vector<int> parinte(get_nrNoduri(), -1);
    vector<int> id(get_nrNoduri(),  INT_MAX);
    vector<bool> vizitat(get_nrNoduri(), false);

    id[0] = 0;

    for (int i = 0; i < get_nrNoduri(); i++){
        // alegem muchia de cost minim ale carei noduri nu au fost adaugate in APM
        int min = INT_MAX;
        int idMin;
        for (int j = 0; j < get_nrNoduri(); j++){
            if (vizitat[j] == false && id[j] < min){
                min = id[j];
                idMin = j;
            }
        }
        vizitat[idMin] = true;

       // for (int j = 0; j < get_nrNoduri(); j++){
          //  if (listaVeciniCost[idMin][j] && vizitat[j] == false && listaVeciniCost[idMin][j] < id[j]){
           //     parinte[j] = idMin;
           //     id[j] = listaVeciniCost[idMin][j];
          //  }
      //  }
    }

    return parinte;
}

void Graf::printAPM(){
    vector<int> parinte = this->APM();
    int suma = 0;

    for (int i = 1; i < listaVeciniCost.size(); i++){
   //     suma += listaVeciniCost[i][parinte[i]];
    }
    g << suma << "\n";

    g << parinte.size() - 1;

    for (int i = 1; i < listaVeciniCost.size(); i++){
        g << "\n" << parinte[i] + 1 << " " << i + 1;
    }
}

void Graf::Dijkstra(){

}


int main(){
    // Graf g1;
    // g1.nrCompConexe();
    // g1.Distante();
    // g1.Tarjan();
    // g1.Kahn();
    //g << HavelHakimi();

    // vector<int> rezAPM =
    // g1.printAPM();
    // int suma = 0;
    // for (int i = 0; i < rezAPM.size(); i++){
    //     suma +=
    // }

    // g1.citireEul();
    // g1.hasEulerCircuit();
    //cout << g1.hasHamiltonCycle();

    int n, m;
    f >> n >> m;

    Graf g1(n);

    int nod1, // primul nod al muchiei
        nod2,
        cost; // al doilea nod al muchiei

    int mat[n][n];

    for (int i = 0; i < n; i++){
        for (int j = 0; j < n; j++){
            mat[i][j] = 0;
        }
    }

    vector<vector<int>> ma;
    for (int i = 0; i < m; i++){
        f >> nod1;
        f >> nod2;
        f >> cost;
        mat[nod1][nod2] = cost;
    }


    for (int i = 0; i < n; i++){
            vector<int> v;
            ma.push_back(v);
        for (int j = 0; j < n; j++){
            ma[i].push_back(mat[i][j]);
        }
    }

    int rez = g1.hamilton(ma);
    if (rez == 0)
        g << "Nu exista solutie";
    else
        g << rez;
    return 0;
}