connected.cpp
// $Id$
/* COMIENZO DE DESCRIPCION
__USE_WIKI__
Dado un grafo como #map<int,set<int>> G# encontrar los subconjuntos del
mismo #list<set<int>> D# que estan desconectados. Por ejemplo, si
#G={1->{2},2->{1},3->{4},4->{3}}#,
entonces debe retornar #D=({1,2},{3,4})#.
La signatura de la funcion a implementar es
#void connected(map<int,set<int>> &G, list<set<int>> &D);#
[Tomado en el 3er parcial 2008-11-20].
keywords: conjunto
FIN DE DESCRIPCION */
#include <time.h>
#include <sys/time.h>
#include <cassert>
#include <cmath>
#include <set>
#include <map>
#include <algorithm>
#include "./util.h"
using namespace std;
//---:---<*>---:---<*>---:---<*>---:---<*>---:---<*>
void print_set(set<int> &s,const char *lab=NULL) {
set<int>::iterator q = s.begin();
if (lab) printf("%s: ",lab);
while (q != s.end())
printf("%d ",*q++);
printf("\n");
}
//---:---<*>---:---<*>---:---<*>---:---<*>---:---<*>
// Busca la componente conexa #W# de un nodo #x# en el grafo
// #G#. Se mantenienen dos conjuntos #W# (los vertices ya
// visitados) y #F# el frente que esta avanzando. Inicialmente
// #W=F={x}#. Ahora para calcular el siguiente frente hacemos
// #Q = \bigcup_{n\in F} G[n]#, (vecinos de #F# )
// #F = Q-W#, (nuevo frente)
// #W = W \cup F#, (actualiza #Q#)
// El algoritmo termina cuando #F# queda vacio.
void connected_to(map<int,set<int> > &G,
int x,set<int> &W) {
// Inicializa F y W
set<int> F;
F.insert(x);
W.clear();
W.insert(x);
// Lazo principal, en cada ejecucion de este lazo el frente
// avanza una capa de vecinos.
while(!F.empty()) {
// Arma Q, el conjunto de los vecinos de los
// nodos en F
set<int> Q;
set<int>::iterator n = F.begin();
while (n!=F.end()) {
set<int> &Gx = G[*n], tmp;
set_union(Q.begin(),Q.end(),
Gx.begin(),Gx.end(),
inserter(tmp,tmp.end()));
swap(Q,tmp);
n++;
}
// Calcula el nuevo frente F = Q - W
set<int> tmp;
set_difference(Q.begin(),Q.end(),
W.begin(),W.end(),
inserter(tmp,tmp.end()));
swap(tmp,F);
// Calcula el nuevo acumulado W = W \cup F
tmp.clear();
set_union(W.begin(),W.end(),
F.begin(),F.end(),
inserter(tmp,tmp.end()));
swap(tmp,W);
}
}
//---:---<*>---:---<*>---:---<*>---:---<*>---:---<*>
// Otra version de `connected_to', mas simple pero menos
// eficiente. Tomamos inicialmente el acumulado `W={x}'. A
// partir de ahi vamos calculando `Wnew' el conjunto de
// todos los vertices connectados a W y despues W=Wnew. El
// algoritmo termina cuando Wnew = W
void connected_to2(map<int,set<int> > &G,
int x,set<int> &W) {
// Inicializa `W'
W.clear();
W.insert(x);
while(1) {
// Calcula Wnew, recorriendo todos los elementos de `W'
int m = W.size();
set<int>::iterator q = W.begin();
set<int> Wnew;
while (q!=W.end()) {
// Agrega todos los conectados a `*q' a `Wnew'
set<int> &ngbq = G[*q++];
set_union(W.begin(),W.end(),
ngbq.begin(),ngbq.end(),
inserter(Wnew,sxnew.end()));
}
// Verifica si el acumulado crecio o no. Si no crecio
// quiere decir que ya tenemos en `W' toda la
// componente conexa.
if (Wnew.size()==W.size()) break;
swap(Wnew,W);
}
}
//---:---<*>---:---<*>---:---<*>---:---<*>---:---<*>
// Determina todas las componentes conexas de G, en la lista
// de conjuntos D. Para esto inicializamos un conjunto
// not_visited=V, es decir con todos los vertices del grafo,
// e ir tomando un elemento `x' de `not_visited', calcular
// su componente conexa `vx' y eliminar `vx' de
// `not_visited'. El algoritmo termina cuando `not_visited'
// queda vacio.
void connected(map<int,set<int> > &G,
list<set<int> > &D) {
// Conjuntos de los nos visitados. Inicialmente es igual al
// conjunto de todos los vertices de `G', es decir de las claves
// de la correspondencia.
set<int> not_visited;
map<int,set<int> >::iterator q = G.begin();
while (q!=G.end()) {
not_visited.insert(q->first);
q++;
}
// print_set(not_visited,"not_visited");
// En cada ejecucion del lazo toma un element de
// `not_visited' calcula su componente conexa `sx' y la
// agrega a `D'. A su vez los elementos de `sx' son eliminados
// de `V'.
while (!not_visited.empty()) {
// Toma un elemento de `not_visited'
int x = *not_visited.begin();
// Inserta un conjunto vacio en `D'. Esta sera la
// nueva componente conexa.
D.insert(D.begin(),set<int>());
set<int> &sx = *D.begin();
// Calcula la componente conexa con `connected_to()'
connected_to(G,x,sx);
// Saca los elementos de `sx' de `not_visited'
set<int> tmp;
set_difference(not_visited.begin(),not_visited.end(),
sx.begin(),sx.end(),
inserter(tmp,tmp.end()));
swap(tmp,not_visited);
}
}
//---:---<*>---:---<*>---:---<*>---:---<*>---:---<*>
int main() {
#if 0
map<int,set<int> > G;
set<int> s;
s.clear(); s.insert(2); G[1] = s;
s.clear(); s.insert(1); G[2] = s;
s.clear(); s.insert(4); G[3] = s;
s.clear(); s.insert(3); G[4] = s;
s.clear();
connected_to(G,1,s);
print_set(s,"connected to 1");
s.clear();
connected_to(G,1,s);
print_set(s,"connected to 2");
s.clear();
connected_to(G,3,s);
print_set(s,"connected to 3");
s.clear();
connected_to(G,4,s);
print_set(s,"connected to 4");
#else
int N = 10, M=5;
map<int,set<int> > G;
vector<int> v(N);
for (int j=0; j<N; j++) v[j] = j;
random_shuffle(v.begin(),v.end());
int k=0, loop=0;
while (k<N) {
int m = 1+rand()%(2*M);
if (k+m>N) m = N-k;
// Next cycle is in range k+[0,n)
printf("loop %d, vertices: ",loop++);
for (int j=0; j<m; j++) {
int k0 = k+j;
printf(" %d",v[k0]);
int km1 = k + (j-1+m) % m;
int kp1 = k + (j+1) % m;
set<int> &Gk = G[v[k0]];
// printf("link %d -> {%d,%d}\n",v[k0],v[km1],v[kp1]);
Gk.insert(v[km1]);
Gk.insert(v[kp1]);
}
printf("\n");
k += m;
}
#endif
map<int, set<int> >::iterator q = G.begin();
while (q!=G.end()) {
printf("%d -> ",q->first);
print_set(q->second);
q++;
}
list<set<int> > D;
connected(G,D);
list<set<int> >::iterator r = D.begin();
while (r!=D.end()) {
print_set(*r++,"disc graph");
}
}
Generated by GNU Enscript 1.6.6.