Cod sursa(job #256799)

Utilizator stocarulCosmin-Mihai Tutunaru stocarul Data 12 februarie 2009 10:51:50
Problema Cuplaj maxim in graf bipartit Scor 24
Compilator c Status done
Runda Arhiva educationala Marime 4.7 kb
#include<stdio.h>
#define infile "cuplaj.in"
#define outfile "cuplaj.out"
#define nmax (20*1000+5)
#define mmax (nmax+100*1000+1)
#define inf ~(1<<31)
struct lista
	{
	int n,p,f; //nodul pozitia si fluxul folosit pe acest arc
	} l[mmax],lt[mmax]; //lista pt graful normal si pt graful transpus
int viz[nmax]; //vector pt vizite
int c[nmax]; //coada
int p[nmax],pt[nmax]; //pozitiile din lista pt graful normal si pt cel transpus
int le,ri; //lungimea celor doua grupuri de noduri
int n,m; //numarul de noduri respectiv muchii
int st,fi; //cele doua noduri pe care le adaugam a.i. sa avem o retea
int flux; //fluxul maxim din cuplaj

int modul(int x)
	{
	if(x<0) return (-1)*x;
	return x;
	}

//adauga arcul (x,y) in lista
void add(struct lista l[mmax], int p[nmax], int m, int x, int y)
	{
	l[m].n=y; l[m].p=p[x]; p[x]=m;
	}

void citire()
	{
	int i,x,y;
	scanf("%d %d %d\n",&le,&ri,&m); //nuymarul nodurilor din fiecare grupa, si numarul de muchii dintre noduri
	for(i=1;i<=m;i++) //fiecare muchie in parte
		{
		scanf("%d %d\n",&x,&y);
		y+=le; //numerotarea nodurilor din grupa 2 incepe de la l
		add(l,p,i,x,y); //graful normal
		add(lt,pt,i,y,x); //graful transpus
		}
	n=le+ri+2; //numarul total de noduri
	}

//transformam graful intr-o retea de transport
void initializare()
	{
	int i;
	st=le+ri+1; fi=st+1; //nuymarul de ordine al celor doua noduri (start si final)
	for(i=1;i<=le;i++) //trebuie sa adaugam arc de la nodul de start la toate nodurile din grupa l
		{
		++m;
		add(l,p,m,st,i); //adaugam in graful normal
		add(lt,pt,m,i,st); //adaugam in graful transpus
		}
	for(i=1;i<=ri;i++)//trebuie sa adaugam arc de la fiecare nod din grupa a 2-a la nodul final
		{
		++m;
		add(l,p,m,le+i,fi); //graful normal
		add(lt,p,m,fi,le+i); //graful transpus
		}
	}

//modifica fluxul pentru un arc din lista
void modifica_flux(struct lista l[mmax], int p[nmax], int x, int y, int f)
	{
	int ul=p[x]; //pozitia copilului
	while(ul) //cat timp are copil
		{
		if(l[ul].n==y) { l[ul].f=f; break; } //am gasit nodul..modific fluxul
		ul=l[ul].p; //frasu
		}
	}

//incercam sa atingem nodul final....din nodul x
int bfs(int x)
	{
	int cs,cd; //pozitiile coade
	int ul;
	c[cs=cd=1]=x; viz[x]=1; //initializam coada
	while(cs<=cd) //cat timp avem elemente in coada
		{
		int x=c[cs]; //nodul caruia trebuie sa ii cautam copii
		for(ul=p[x];ul;ul=l[ul].p) //trecem la toti copii lui x
			if(!viz[l[ul].n]&&!l[ul].f) //daca nodul nu a mai fost vizitat, si daca arcul nu este saturat
				{
				c[++cd]=l[ul].n; //il adaugam in coada
				viz[l[ul].n]=x; //il marcam ca vizitat, si ca venim de la x
				if(l[ul].n==fi) return 1; //am atins nodul final
				}
		for(ul=pt[x];ul;ul=lt[ul].p) //copii din graful transpus
			if(!viz[lt[ul].n]&&lt[ul].f) //daca nodul nu a fost vizitat, si daca avem flux (parcurgerea este DESTINATIE-SRUSA)
				{
				c[++cd]=lt[ul].n; //adaugam in coada
				viz[lt[ul].n]=-x; //il marcam vizitat...si cu minus pt k este in sens invers
				if(lt[ul].n==fi) return 1; //am atins nodul final
				}
		cs++; //inaintam in coada
		}
	return 0; //daca am ajuns aici, inseamna k nu am atins nodul final
	}

void calculeaza_flux()
	{
	int s[nmax]; //stiva in care refacem lantul de ameliorare
	int i,f=1; //fluxul oricarui lant este egal cu 1
	while(bfs(st)) //cat timp atingem nodul final
		{
		s[s[0]=1]=fi; //initial avem nodul final
		do
			{
			s[++s[0]]=modul(viz[s[s[0]-1]]); //adaugam in lant pe cel de la care vine
			if(viz[s[s[0]]]>0) //lantul se parcurge in mod normal
				{
				modifica_flux(l,p,s[s[0]],s[s[0]-1],1); //salvam fluxul pe lant
				modifica_flux(lt,pt,s[s[0]-1],s[s[0]],1); //salvam fluxul pe lant in cel transpus
				}
			else //inseamna k lantul se parcurge in sens invers
				{
				modifica_flux(l,p,s[s[0]],s[s[0]-1],0); //salvam fluxul pe lant
				modifica_flux(lt,pt,s[s[0]-1],s[s[0]],0); //salvam fluxul pe lantul transpus
				}
			}
		while(s[s[0]]!=st); //pana nu am adaugat nodul initial..refacem lantul
		flux++; //crestem fluxu;
		for(i=1;i<=n;i++) viz[i]=0; //golim vectorul viz
		}
	}

void afisare()
	{
	int i,ul;
	printf("%d\n",flux); //afisem fluxul maxim
	for(i=1;i<=le;i++) //luam fiecare nod din primul grup
		{
		ul=p[i]; //ii luam pozitia din lista
		while(ul)
			{
			if(l[ul].f==1) { printf("%d %d\n",i,l[ul].n-le); break; }//afisem muchia si oprim 
			ul=l[ul].p; //frasu
			}
		}
	}

int main()
{
freopen(infile,"r",stdin);
freopen(outfile,"w",stdout);

citire(); //citim graful
initializare(); //transformam graful in retea de transport
calculeaza_flux(); //facem reteaua pentru flux maxim (adica legaturile ...cuplajul)
afisare(); //afisem

fclose(stdin);
fclose(stdout);
return 0;
}