// Popescu Paullo Robertto Karloss Grupa 2311
// Linkurile pentru fiecare problema se gasesc in main
#include <fstream>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <algorithm>
#include <climits>
#define INFINIT INT_MAX
using namespace std;
ifstream fin("ctc.in");
ofstream fout("ctc.out");
struct muchie {
int destinatie, cost, capacitate;
muchie(int destinatie, int cost, int capacitate) : destinatie(destinatie), cost(cost), capacitate(capacitate) {}
};
class Graf {
private:
int nrNoduri, nrMuchii;
bool eOrientat, arePonderi, areCapacitati;
vector<vector<muchie>> listaDeAdiacenta; // lista de vecini
public:
Graf(const int nrNoduri, const int nrMuchii, const bool eOrientat, const bool arePonderi, const bool areCapacitati);
void citire();
vector<int> numarMinimArce(int &nodPlecare);
int numarComponenteConexe();
vector<set<int>> componenteBiconexe();
pair<int, int> componenteTareConexe();
void afis();
~Graf();
private:
void BFSrecursiv(queue<int> &coada, vector<int> &vizitat);
void DFSrecursiv(int &nodPlecare, vector<int> &vizitat);
void biconex(int nodPlecare, int precedent, int k, stack<pair<int, int>> &stivaComponenteBiconexe,
vector<set<int>> &componenteBiconexe, vector<int> &vizitat, vector<int> &niv_min);
vector<vector<muchie>> transpus();
void DF1(int nodPlecare, vector<int> &succesor, vector<int> &v, int &index);
void DF2(int nodPlecare, vector<int> &predecesor, int &nrComponenteTareConexe,
vector<vector<muchie>> listaDeAdiacentaTranspusa);
};
void Graf::BFSrecursiv(queue<int> &coada, vector<int> &vizitat) {
if (!coada.empty()) // daca mai sunt elemente in coada / nu am verificat pt toate nodurile
{
int nodPlecare = coada.front(); // retin nodul de unde plec
for (auto &i: listaDeAdiacenta[nodPlecare])
if (vizitat[i.destinatie] == -1) {
// caut toate nodurile nevizitate care sunt adiacente cu nodul de plecare
vizitat[i.destinatie] = vizitat[nodPlecare] + 1; // il marcam vizitat
coada.push(i.destinatie); // il adaug in coada PUSH
}
coada.pop();
BFSrecursiv(coada, vizitat);
}
}
vector<int> Graf::numarMinimArce(int &nodPlecare) {
vector<int> vizitat(nrNoduri + 1, -1);
queue<int> coada;
coada.push(nodPlecare);
vizitat[coada.back()] = 1;
BFSrecursiv(coada, vizitat);
for (int i = 1; i <= nrNoduri; i++) {
if (vizitat[i] != -1)
vizitat[i] -= 1;
}
return vizitat;
}
void Graf::DFSrecursiv(int &nodPlecare, vector<int> &vizitat) {
vizitat[nodPlecare] = 1;
for (auto &i: listaDeAdiacenta[nodPlecare])
if (!vizitat[i.destinatie])
DFSrecursiv(i.destinatie, vizitat);
}
int Graf::numarComponenteConexe() {
// vector<int> nivel(nrNoduri + 1, 0);
vector<int> vizitat(nrNoduri + 1, 0);
// nivel[1] = 0;
int numar = 0;
for (int i = 1; i <= nrNoduri; i++)
if (vizitat[i] == 0) {
numar++;
DFSrecursiv(i, vizitat);
}
return numar;
}
void Graf::biconex(int nodPlecare, int precedent, int k, stack<pair<int, int>> &stivaComponenteBiconexe,
vector<set<int>> &componenteBiconexe, vector<int> &vizitat, vector<int> &niv_min) {
vizitat[nodPlecare] = k;
niv_min[nodPlecare] = k;
for (auto &i: listaDeAdiacenta[nodPlecare]) {
int vecin = i.destinatie;
if (vecin != precedent) { // pentru optimizare (iese din timp altfel, uneori),
// daca vecinul curent nu s-a executat la pasul anterior
if (!vizitat[vecin]) { // daca vecinul nu a fost vizitat
stivaComponenteBiconexe.push(make_pair(nodPlecare, vecin));
biconex(vecin, nodPlecare, k + 1, stivaComponenteBiconexe, componenteBiconexe, vizitat,
niv_min); // reapelez DF din nodul in care am ajuns
if (niv_min[nodPlecare] > niv_min[vecin]) // daca face parte din ciclu
niv_min[nodPlecare] = niv_min[vecin]; // actualizez nivelul minim
if (niv_min[vecin] >= vizitat[nodPlecare]) {
// daca un vecin are nivelul minim mai mare sau egal decat nivelul tatalui sau
// (vizitat este pe pos de nivel din muchia critica, i.e. nivelul din arborele DF),
// inseamna ca nu face parte dintr-un ciclu, deci am gasit o componenta biconexa
set<int> aux;
int aux1, aux2;
do {
aux1 = stivaComponenteBiconexe.top().first;
aux2 = stivaComponenteBiconexe.top().second;
aux.insert(aux1);
aux.insert(aux2);
stivaComponenteBiconexe.pop();
} while (aux1 != nodPlecare || aux2 != vecin);
componenteBiconexe.push_back(aux);
}
} else if (niv_min[nodPlecare] > vizitat[vecin]) { // daca nodul curent a fost deja vizitat
// si daca exista o muchie de intoarcere de la nodPlecare la nodul curent, exista legatura cu un stramos
// (muchie de intoarcere de la nodPlecare la nodul curent)
niv_min[nodPlecare] = vizitat[vecin]; // nivelul nodului de Plecare
// va fi nivelul stramosului sau (nodul deja vizitat)
}
}
}
}
vector<set<int>> Graf::componenteBiconexe() {
stack<pair<int, int>> stivaComponenteBiconexe;
vector<set<int>> componente;
vector<int> vizitat(nrNoduri + 1, 0);
vector<int> niv_min(nrNoduri + 1, 0);
biconex(1, 0, 1, stivaComponenteBiconexe, componente, vizitat, niv_min);
return componente;
}
vector<vector<muchie>> Graf::transpus() {
// Graf g(nrNoduri, nrMuchii, eOrientat, false, false);
vector<vector<muchie>> listaDeAdiacentaTranspusa(nrNoduri + 1);
for (int i = 1; i <= nrNoduri; i++)
for (auto &j: listaDeAdiacenta[i])
// g.listaDeAdiacenta[j.destinatie].push_back(muchie(i, j.cost, j.capacitate));
listaDeAdiacentaTranspusa[j.destinatie].push_back(muchie(i, j.cost, j.capacitate));
return listaDeAdiacentaTranspusa;
}
void Graf::DF1(int nodPlecare, vector<int> &succesor, vector<int> &v, int &index) {
succesor[nodPlecare] = 1; // marchez nodul succesor nodului curent ca fiind vizitat
for (auto &i: listaDeAdiacenta[nodPlecare]) // parcurg toti vecinii nodului
if (!succesor[i.destinatie]) // daca succesorul nu a fost vizitat
DF1(i.destinatie, succesor, v, index); // continui parcurgerea
v[++index] = nodPlecare; // retin succesorii intr-un array
}
void Graf::DF2(int nodPlecare, vector<int> &predecesor, int &nrComponenteTareConexe,
vector<vector<muchie>> listaDeAdiacentaTranspusa) {
predecesor[nodPlecare] = nrComponenteTareConexe; // marchez nodul predecesor nodului curent ca fiind vizitat
for (auto &i: listaDeAdiacentaTranspusa[nodPlecare])
if (!predecesor[i.destinatie])
DF2(i.destinatie, predecesor, nrComponenteTareConexe, listaDeAdiacentaTranspusa);
}
// Am folosit algoritmul lui Kosaraju pentru a afla numarul de Componente Tare Conexe intr-un graf orientat
pair<int, int> Graf::componenteTareConexe() {
vector<vector<muchie>> listaDeAdiacentaTranspusa = transpus();// lista de vecini transpusa
// (i.e. in loc de sursa si destinatie folosim destinatie si sursa ca la grafuri neorientate)
vector<int> succesor(nrNoduri + 1, 0);
vector<int> predecesor(nrNoduri + 1, 0);
vector<int> v(nrNoduri + 1, 0);
pair<int, int> p[nrNoduri + 1]; // pereche (predecesor, nod)
int nrComponenteTareConexe = 1, index = 0;
for (int i = 1; i <= nrNoduri; i++)
if (succesor[i] == 0) // daca nodul i nu a fost vizitat
DF1(i, succesor, v, index); // parcurg in adancime marcand succesorii
for (int i = nrNoduri; i >= 1; i--)
if (predecesor[v[i]] == 0) // daca predecesorul lui i nu a fost vizitat
{
DF2(v[i], predecesor, nrComponenteTareConexe,
listaDeAdiacentaTranspusa); // parcurg in adancime marcand predecesorii
nrComponenteTareConexe++;
}
fout << nrComponenteTareConexe - 1 << '\n';
for (int i = 1; i <= nrNoduri; i++) {
p[i].first = predecesor[i]; // predecesorul nodului curent
p[i].second = i; // valoarea nodului curent
}
sort(p + 1, p + nrNoduri + 1); // sortez crescator dupa predecesor
// return p;
for (int i = 1; i <= nrNoduri; i++) {
if (p[i].first != p[i + 1].first) {
fout << p[i].second << '\n';
} else {
fout << p[i].second << " ";
}
}
}
void rezolvaBFS() {
// Problema BFS (100p)
// Link: https://infoarena.ro/problema/bfs
// Sursa: https://infoarena.ro/job_detail/2797664?action=view-source
int nrNoduri, nrMuchii, nodPlecare;
fin >> nrNoduri >> nrMuchii >> nodPlecare;
Graf g1(nrNoduri, nrMuchii, true, false, false);
g1.citire();
vector<int> distante = g1.numarMinimArce(nodPlecare);
for (int i = 1; i < distante.size(); i++)
fout << distante[i] << " ";
fin.close();
fout.close();
}
void rezolvaDFS() {
// Problema DFS (100p)
// Link: https://infoarena.ro/problema/dfs
// Sursa: https://infoarena.ro/job_detail/2797669?action=view-source
int nrNoduri, nrMuchii;
fin >> nrNoduri >> nrMuchii;
Graf g1(nrNoduri, nrMuchii, false, false, false);
g1.citire();
int numarComponenteConexe = g1.numarComponenteConexe();
fout << numarComponenteConexe;
fin.close();
fout.close();
}
void rezolvaComponenteBiconexe() {
// Problema Componente Biconexe (100p)
// Link: https://infoarena.ro/problema/biconex
// Sursa: https://infoarena.ro/job_detail/2797675?action=view-source
int nrNoduri, nrMuchii;
fin >> nrNoduri >> nrMuchii;
Graf g1(nrNoduri, nrMuchii, false, false, false);
g1.citire();
set<int>::iterator it;
vector<set<int>> componente = g1.componenteBiconexe();
fout << componente.size() << "\n";
for (auto &i: componente) {
for (it = i.begin(); it != i.end(); it++) {
fout << *it << " ";
}
fout << "\n";
}
fin.close();
fout.close();
}
void rezolvaComponenteTareConexe() {
// Problema CTC (Componente Tare Conexe) (100p)
// Link: https://infoarena.ro/problema/ctc
// Sursa: https://infoarena.ro/job_detail/2797676?action=view-source
int nrNoduri, nrMuchii;
fin >> nrNoduri >> nrMuchii;
Graf g1(nrNoduri, nrMuchii, true, false, false);
g1.citire();
g1.componenteTareConexe();
fin.close();
fout.close();
}
int main() {
int optiune = 4;
switch (optiune) {
case 1:
rezolvaBFS();
case 2:
rezolvaDFS();
case 3:
rezolvaComponenteBiconexe();
case 4:
rezolvaComponenteTareConexe();
}
return 0;
}
void Graf::afis() {
for (int i = 1; i <= nrNoduri; i++) {
fout << "Nodul " << i << " este adiacent cu: ";
for (auto &j: listaDeAdiacenta[i])
fout << j.destinatie << " cu costul " << j.cost << " cu capacitatea " << j.capacitate << "\n";
fout << "break\n";
}
}
void Graf::citire() {
int sursa, destinatie, cost, capacitate;
for (int i = 1; i <= nrMuchii; i++) {
fin >> sursa >> destinatie;
if (arePonderi)
fin >> cost;
else
cost = 0;
if (areCapacitati)
fin >> capacitate;
else
capacitate = 0;
listaDeAdiacenta[sursa].push_back(muchie(destinatie, cost, capacitate));
if (!eOrientat)
listaDeAdiacenta[destinatie].push_back(muchie(sursa, cost, capacitate));
}
}
Graf::Graf(const int nrNoduri, const int nrMuchii, const bool eOrientat, const bool arePonderi,
const bool areCapacitati) {
this->nrNoduri = nrNoduri;
this->nrMuchii = nrMuchii;
this->eOrientat = eOrientat;
this->arePonderi = arePonderi;
this->areCapacitati = areCapacitati;
listaDeAdiacenta.resize(nrNoduri + 1);
}
Graf::~Graf() {
listaDeAdiacenta.clear();
}