Cod sursa(job #2962236)

Utilizator miruna_georgescuMiruna Georgescu miruna_georgescu Data 8 ianuarie 2023 01:46:21
Problema Taramul Nicaieri Scor 100
Compilator cpp-64 Status done
Runda Arhiva de probleme Marime 5.41 kb
/*
	Taramul Nicaieri   https://www.infoarena.ro/problema/harta
		Harta "Taramului nicaieri" a fost pierduta, dar din fericire se mai stiu cateva date despre ea. Se stie ca exista N orase intre care se afla diferite drumuri. Drumurile
	"Taramului nicaieri" sunt un pic mai ciudate deoarece daca exista un drum care pleaca din orasul i si ajunge in orasul j nu exista in mod obligatoriu si un drum care pleaca din
	orasul j si ajunge in orasul i. De asemenea nu vor exista drumuri care pleaca si ajung in acelasi oras. Si nu vor exista mai multe drumuri identice(adica sa coincida si orasul din
	care pleaca si cel in care se ajunge).Pentru fiecare oras se stie cate drumuri pleaca din el si cate drumuri ajung in el.

	Cerinta
		Scrieti un program care determina drumurile de pe harta initiala.

	Date de Intrare
		Prima linie a fisierului de intrare harta.in contine numarul intreg N, reprezentand numarul de orase. Urmeaza apoi N linii, linia i continand doua numere x,y numarul de drumuri
	care pleaca din orasul i respectiv numarul de drumuri care intra in orasul i.

	Date de Iesire
		In fisierul harta.out veti afisa pe prima linie numarul M de drumuri construite, apoi vor urma M linii pe fiecare aflandu-se o pereche de numere a,b cu semnificatia exista un
	drum care pleaca din a si ajunge in b.
*/

#include <fstream>
#include <vector>
#include <queue>
#include <climits>

using namespace std; 

int nrOrase, nodInceput, nodSfarsit;
vector<vector <int>> listaAdiacenta, capacitateReziduala; 

//functia pentru citirea datelor: 
//orasele reprezinta nodurile unui grad orientat, iar pentru determinarea arcelor (drumurilor) pe harta se va calcula fluxul maxim 
//pentru a rezolva problema trebuie adaugate doua noduri noi in graf si anume nodul de inceput si nodul de sfasit, de asemenea nodurile care reprezinta orasele se vor dubla, iar fiecare nod din 
//multimea initiala de noduri se va lega de toate celelalte dubluri, mai putin de propria dublura
void citire()
{
	ifstream fin("harta.in");
	fin >> nrOrase;

	nodInceput = 0; 
	nodSfarsit = 2 * nrOrase + 1; 

	listaAdiacenta.resize(2 * nrOrase + 10); 
	capacitateReziduala.resize(2 * nrOrase + 10, vector<int>(2 * nrOrase + 10));
	for(int i = 1; i <= nrOrase; i++)
		for(int j = 1; j <= nrOrase; j++)
			if (i != j)
			{
				listaAdiacenta[i].push_back(j + nrOrase); 
				listaAdiacenta[j + nrOrase].push_back(i);
				capacitateReziduala[i][j + nrOrase] = 1; 
			}

	int gradIntrare, gradIesire; 
	for (int i = 1; i <= nrOrase; i++)
	{
		fin >> gradIntrare >> gradIesire; 

		listaAdiacenta[nodInceput].push_back(i); 
		listaAdiacenta[i].push_back(nodInceput); 
		capacitateReziduala[nodInceput][i] = gradIntrare; 

		listaAdiacenta[nodSfarsit].push_back(i + nrOrase);
		listaAdiacenta[i + nrOrase].push_back(nodSfarsit);
		capacitateReziduala[i + nrOrase][nodSfarsit] = gradIesire;
	}


	fin.close();
}

vector<bool> vizitat;
vector <int> tata;

//in cadrul parcurgerii BFS caut lanturi de la nodul de inceput (nodInceput) la nodul final (nodSfarsit) in care fiecare muchie sa aiba capacitatea reziduala mai mica decat capacitatea initiala
bool BFS()
{
	tata.clear();
	tata.resize(2 * nrOrase + 10, 0);

	vizitat.clear();
	vizitat.resize(2 * nrOrase + 10, false);

	queue<int> vecini;
	vecini.push(nodInceput);
	vizitat[nodInceput] = true;
	tata[nodInceput] = -1; 

	while (!vecini.empty())
	{
		int nodCurent = vecini.front();
		vecini.pop();

		if (nodCurent == nodSfarsit) //am ajuns la nodul de sfasit, asadar am gasit un lant care respecta criteriile impuse, deci ies din structura repetitiva
			continue;

		for (int vecin : listaAdiacenta[nodCurent])
			if (!vizitat[vecin] && capacitateReziduala[nodCurent][vecin] > 0)
			{
				tata[vecin] = nodCurent;
				vecini.push(vecin);
				vizitat[vecin] = true;
			}
	}

	return vizitat[nodSfarsit];
}

int fluxMaxim; 

void EdmondsKarp()
{
	//pentru gasirea drumurilor de ameliorare (drum de la sursa la destinatie, in care fiecare muchie de pe drum sa aiba fluxul asociat pana in acest moment strict mai mic decat capacitatea sa) folosesc parcurgere BFS
	//cat timp exista astfel de drumuri de ameliorare, le parcurg, adaugand flux
	while (BFS())
	{
		//penrtu fiecare drum de ameliorare gasit, caut bottleneck-ul (valoarea minima cu care poate fi marit fluxul asociat fiecarei muchii care se afla pe drumul gasit) si updatez capacitatile reziduale 
		for (int vecin : listaAdiacenta[nodSfarsit])
		{
			if (vizitat[vecin] && capacitateReziduala[vecin][nodSfarsit] > 0)
			{
				int fluxCrt = capacitateReziduala[vecin][nodSfarsit];
				for (int nodCrt = vecin; nodCrt != nodInceput; nodCrt = tata[nodCrt])
					fluxCrt = min(fluxCrt, capacitateReziduala[tata[nodCrt]][nodCrt]);

				capacitateReziduala[vecin][nodSfarsit] -= fluxCrt;
				capacitateReziduala[nodSfarsit][vecin] += fluxCrt;
				for (int nodCrt = vecin; nodCrt != nodInceput; nodCrt = tata[nodCrt])
				{
					capacitateReziduala[tata[nodCrt]][nodCrt] -= fluxCrt;
					capacitateReziduala[nodCrt][tata[nodCrt]] += fluxCrt;
				}

				fluxMaxim += fluxCrt;
			}
		}
	}

}

void afisare()
{
	ofstream fout("harta.out");

	fout << fluxMaxim << "\n"; 
	for (int i = 1; i <= nrOrase; i++)
		for (int j = nrOrase + 1; j <= 2 * nrOrase; j++)
			if (capacitateReziduala[j][i] == 1)
				fout << i << " " << j - nrOrase << "\n"; 

	fout.close(); 
}

int main()
{
	citire(); 
	EdmondsKarp();
	afisare(); 
}