Cod sursa(job #2961879)

Utilizator ccazacuCazacu Cristian ccazacu Data 7 ianuarie 2023 12:02:10
Problema Cuplaj maxim in graf bipartit Scor 100
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 4.54 kb
#include <iostream>
#include <bits/stdc++.h>
using namespace std;

ifstream fin("cuplaj.in");
ofstream fout("cuplaj.out");

//Problema cuplajului iin graf bipartit se rezolva cu ajutorul algoritmului de determinare
//al fluxului intr-o retea de transport
//ca sa facem asta, adaugam un nod de start 0 si unul de destinatie n+m+1, si nodul de start il conectam
//la toate nodurile din prima grupa cu capacitate 0, iar nodurile din a doua grupa le conectam la destinatie cu capacitate 1
//de asemenea, punem capacitate 1 intre toate nodurile ce duc din grupa 1 in grupa 2
//calculam flux maxim pe reteaua creata si apoi muchiile ce duc din prima grupa in a doua care au fluxul maxim(1), sunt
//muchiile care fac parte din cuplajul maxim
//fluxul maxim e cuplajul maxim
int n, m, e, N;
int viz[20001];
pair<int, int>tata[20001];//ca sa retin nodurile vizitate si tatal nodurilor pentru reconstruirea drumului
//pentru tata retinem nodul si pozitia unde s-ar afla tatal in lista nodului curent
vector<vector<pair<pair<int, int>, int>>> lista(20001);//lista de muchii (vecin, capacitate, pozitia din lista vecinului care ar indica spre nodul respectiv)

int bfs(){//bfs pentru aflarea existentei drumului de crestere
    queue<int> coada;
    coada.push(0);
    for(int i = 0; i <= N; i++){
        tata[i] = {-1, -1};
        viz[i] = 0;
    }
    viz[0]++;

    while(coada.size()!=0){
        int nod_cur = coada.front();
        if(nod_cur == N)//daca am ajuns la final inseamna ca am gasit un drum de crestere
            return 1;
        int cnt = 0;//ca sa retinem pt fiecare vecin pozitia pe care o aveau in lista nodului
        for(int i = 0; i < lista[nod_cur].size(); i++){
            int nod_vec = lista[nod_cur][i].first.first;
            if(lista[nod_cur][i].first.second > 0 && viz[nod_vec] == 0){//daca am gasit un nod prin care nu am mai fost care mai are loc, trecem prin el
                viz[nod_vec]++;
                tata[nod_vec] = {nod_cur, cnt};//retinem tatal si pozitia
                coada.push(nod_vec);

            }
            cnt++;
        }
        coada.pop();//scot din coada nodul prin care deja am trecut
    }
    return 0;//nu am gasit
}

int Edmonds_Karp(){
    int max_flux = 0;//fluxul maixim
    while(bfs()){//cat timp avem drum de crestere
        for(int i = 0; i < lista[N].size(); i++){
            int nod_cur = lista[N][i].first.first;
            if(lista[nod_cur][lista[N][i].second].first.second > 0 && viz[nod_cur]){//daca este drum care a dus de la vecinul lui n la n care mai are capacitate
                //si a fost vizitat in verificarea noastra, atunci reconstituim drumul parcurs
                int minim = 9999999;//bottleneckul nostru
                tata[N] = {nod_cur, lista[N][i].second};
                int drum = N;
                while(tata[drum].first!=-1){//traversam invers drumul de crestere de la destinatie la start ca sa gasim bottleneck ul
                    minim = min(minim, lista[tata[drum].first][tata[drum].second].first.second);
                    drum = tata[drum].first;
                }


                max_flux += minim;//il adaugam la flux doarece e cea mai mare valoare pe care o putem trece prin acel drum
                drum = N;
                while(tata[drum].first!=-1){//reluam drumul de crestere ca sa actualizam capacitatile nodurilor
                    lista[tata[drum].first][tata[drum].second].first.second -= minim;
                    lista[drum][lista[tata[drum].first][tata[drum].second].second].first.second += minim;
                    drum = tata[drum].first;
                }

            }
        }
    }
    return max_flux;//fluxul maxim

}


int main()
{
    fin>>n>>m>>e;
    N = n + m + 1;
    //creem lista de adiacenta de care am vorbit
    for(int i = 0; i < e; i++){
        int u, v;
        fin>>u>>v;
        lista[u].push_back({{v+n ,1}, lista[v+n].size()});
        lista[v+n].push_back({{u, 0}, lista[u].size()-1});
    }
    for(int i = 1; i <= n; i++){
        lista[0].push_back({{i, 1}, lista[i].size()});
        lista[i].push_back({{0, 0}, lista[0].size()-1});
    }
    for(int i = 1; i <= m; i++){
        lista[i+n].push_back({{n+m+1, 1}, lista[N].size()});
        lista[n+m+1].push_back({{i+n, 0}, lista[i+n].size()-1});
    }
    fout<<Edmonds_Karp()<<"\n";//cuplaj maxim

    for(int i = 1; i <= n; i++)//gasim nodurile care au capacitatea ocupata care duc din prima grupa in a doua
        for(int j = 0; j <lista[i].size(); j++){
            if(lista[i][j].first.second != 1 && lista[i][j].first.first != 0)
                fout<<i<<" "<<lista[i][j].first.first - n<<"\n";
        }
}