Clasa a XI-a lecția 15

From Algopedia
Jump to navigationJump to search

BFS

Se parcurge vârful de start, apoi vecinii acestuia, apoi vecinii nevizitați ai acestora, etc, până când sunt vizitate toate vârfurile accesibile. Practic, pentru a stabili ordinea de vizitare se folosește o coadă, iar pentru a stabili dacă un vârf a fost sau nu vizitat se foloseşte un vector caracteristic. Algoritmul este: adaugăm în coadă vârful inițial și îl vizităm cât timp coada este nevidă extragem un element din coadă determinăm vecinii nevizitați ai vârfului extras, îi vizităm și îi adăugăm în coadă eliminăm elementul din coadă Observație Dacă graful nu este conex, în urma parcurgerii nu se vor vizita toate vârfurile.

Vârfurile grafului au fost parcurse în ordinea: 5 2 4 7 1 3 6 8 9.\ În urma parcurgerii în latime, muchiile folosite pentru parcurgere formează un arbore. Acest arbore este graf parțial al grafului dat (dacă graful este conex), și se numește arbore parțial de parcurgere. Pentru graful de mai sus, arborele de parcurgere pornind din vârful 5 este:

Acest arbore poate fi considerat arbore cu rădăcină, aceasta fiind chiar vârful de start, 5.

În acest caz, odată cu parcurgerea se poate construi și vectorul de “tați” t[] al arborelui. În acest exemplu el este:

k 1 2 3 4 5 6 7 8 9 t[k] 2 5 2 5 0 4 5 7 7

Din vectorul de tați al unui arbore se poate determina ușor pentru un vârf oarecare un lanț de la acel vârf la rădăcina arborelui. Arborii obținuți în urma parcurgerii în lățime au proprietatea că lanțul de la vârful de pornire la oricare vârf accesibil din graf obținut din arborele de parcurgere are lungime minimă.

BFS Nerecursiv

void BFS( nod de start ){                              
  marcam ca vizitat nodul de start
  punem nodul de start in coada
  while( avem elem in coada ){                                
    parcurg toti vecinii primului nod din coada si pentru fiecare vecin nevizitat al acestuia
      il marchez ca vizitat
      il pun in coada
    sterg din coada nodul curent/trec la urm element din coada
  }
}

BFS Recursiv

Inainte de a apela functia, vom pune nodul de start in coada, marcandu-l ca vizitat

void BFS( ){                              
  if (coada nu e vida) 
    extrag primul nod din coada si il tiparesc                              
    parcurg toti vecinii acestuia si pentru fiecare vecin nevizitat 
      il marchez ca vizitat
      il pun in coada
    scot din coada primul element
    reapelez functia
}

BFS Nerecursiv - pe matrice de adiacenta, implementare coada pe vector

#include <fstream>

using namespace std;
ifstream in("BFS.in");
ofstream out("BFS.out");

int n, m, i, j, x;
int a[1001][1001];
int c[1001];         // coada in care tin nodurile in ordinea vizitarii lor
int v[1001];         // vector de frecventa in care marchez daca am vizitat un nod

void BFS( int start ){                             // pornim parcurgerea de la un nod dat
  int i = 1;
  int vf = 0;
  v[start] = 1;                                    // marchez ca vizitat nodul start
  c[++vf] = start;                                 // punem in coada nodul de la care pornim
  while( i <= vf ){                                // cata vreme mai am elemente in coada
    int nod = c[i];                                // nodul curent
    out << nod << " ";                             // afisez nodul curent
    for(int vecin = 1; vecin <= n; vecin++ )       // parcurg toti vecinii nodului curent
      if( a[nod][vecin] == 1 && v[vecin] == 0 ){   // daca vecinii nu au fost vizitati
        v[vecin] = 1;                              // mstchez vecinul ca vizitat
        c[++vf] = vecin;                           // ii pun in coada
      }
    i++;                                           // trec la urmatorul element din coada
  }
}

int main(){
  in >> n >> m >> x;
  for( int l = 1; l <= m; l++ ){
    in >> i >> j;
    a[i][j] = a[j][i] = 1;
  }
  BFS( x );
  return 0;
}

BFS Nerecursiv - reprezentare cu matrice de adiacenta, implementare coada din queue

#include <fstream>
#include <queue>
using namespace std;
ifstream in( "BFS.in" );
ofstream out( "BFS.out" );
int mat[101][101], n;
bool viz[101];              // vectorul in care marcam daca un nod a fost vizitat sau nu
queue<int> q;               // coada in care pun nodurile in ordinea vizitarii
void bfs( int start ){
  q.push(start);                           // punem notul x in stiva lis
  viz[start] = 1;                          // marcam ca vizitat nodul x in strun vect de frecv
  out << start;                            // facem afisarea pe masura ce parcurgem
  while( q.size() ) {                      // cata vreme am elem in coada
    for( int i = 1; i <= n; i++ ){         // parcurg toti vecinii si ii pun in coada pe cei nevizitati
      int nod = q.front();
      if( mat[nod][i] && !viz[i] ){        // pe poz 0 in coada voi avea tot timpul elementul curent
      	q.push(i);                         // pun vecinul in coada
       	out << " " << i;                   // afizez vecinul
        viz[i] = 1;                        // il marchez ca vizitat
      }
    }
    q.pop();	// scot din coada modul pe care tocmai l-am afisat si caruia i-am parcurs toti vecinii
  }
}

int main(){
  int m, i, a, b, start;
  in >> n >> m >> start;         // start = nodul de la care incepem parcurgerea bfs
  for( i = 0; i < m; i++ ) {
    in >> a >> b;
    mat[a][b] = 1;
    mat[b][a] = 1;
  }
  bfs( start );
  return 0;
}

BFS Rerecursiv - reprezentare cu matrice de adiacenta

Varianta 1 de implementare cu coada pe vector

// BFS recursiv, matrice de adiacenta
#include <fstream>

using namespace std;
ifstream in ("bfs.in");
ofstream out ("bfs.out");

int a[102][102], n, sf, k;    // sf - sfarsitul cozii, k -nodul curent
bool viz[102];                // pentru fiecare nod marcam daca a fost vizitat
int c[102];                   // coada care retine nodurile in ordinea vizitarii

void bfs(){
  if ( k <= sf ){                 // daca coada nu e vida
    int nod = c[k];               // extrag primul element si il tiparesc
    out << nod << " ";            // afisez elem
    for (int i = 1 ; i <= n; i++)    // pun in coada toti vecinii nevizitati ai lui nod
      if ( a[nod][i] == 1 && viz[i] == 0 ){
      c[++sf]= i;
      viz[i] = 1;
    }
    k++;
    bfs();
  }
}

int main(){
  int m, start;
  in >> n >> m >> start;

  for ( int i = 1; i <= m; i++ ) {
    int x, y;
    in >> x >> y;
    a[x][y] = a[y][x] = 1;
  }

  // incep parcurgerea grafului de la nodul start
  c[1] = start;          // pun nodul start in coada
  viz[start] = 1;        // il marchez ca l-am vizitat
  k = sf = 1;            // k - nodul pe care ma gasesc, sf sfarsitul cozii in care pun nodurile ce urmeaza a fi vizitate
  bfs();

  return 0;

}

Varianta de implementare cu coada din queue

// folosind coada, recursiv
#include <fstream>
#include <queue>

using namespace std;
ifstream in ("BFS.in");
ofstream out ("BFS.out");
queue<int> q;                         // definim q o coada de int-uri
int a[102][102], n;
bool viz[102];                        // pentru fiecare nod marcam daca a fost vizitat

void bfs(){
  if ( !q.empty() ){                  // daca coada nu e vida
    int nod = q.front();              // extrag primul element si il tiparesc
    out << nod << " ";
    for (int i = 1 ; i <= n; i++){    // pun in coada toti vecinii mevizitati primului element din coada
      if ( a[nod][i] == 1 && viz[i] == 0 ){
        q.push(i);
        viz[i] = 1;
      }
    }
    q.pop();
    bfs();
  }
}

int main(){
  int m, start;
  in >> n >> m >> start;
 
  for ( int i = 1; i <= m; i++ ) {
    int x, y;
    in >> x >> y;
    a[x][y] = a[y][x] = 1;
  }

  q.push(start);         
  viz[start] = 1;
  bfs();

  return 0;

}


BFS Rerecursiv - reprezentare cu matrice de adiacenta, implementare coada din queue

// folosind coada, recursiv
#include <fstream>
#include <queue>

using namespace std;
ifstream in ("BFS.in");
ofstream out ("BFS.out");
queue<int> q;                         // definim q o coada de int-uri
int a[102][102], n;
bool viz[102];                        // pentru fiecare nod marcam daca a fost vizitat

void bfs(){
  if ( !q.empty() ){                  // daca coada nu e vida
    int nod = q.front();              // extrag primul element si il tiparesc
    out << nod << " ";
    for (int i = 1 ; i <= n; i++){    // pun in coada toti vecinii mevizitati primului element din coada
      if ( a[nod][i] == 1 && viz[i] == 0 ){
        q.push(i);
        viz[i] = 1;
      }
    }
    q.pop();
    bfs();
  }
}

int main(){
  int m, start;
  in >> n >> m >> start;
 
  for ( int i = 1; i <= m; i++ ) {
    int x, y;
    in >> x >> y;
    a[x][y] = a[y][x] = 1;
  }

  q.push(start);         
  viz[start] = 1;
  bfs();

  return 0;

}

TEMA BFS

Tema Teorie + Laborator

Implementare BFS


Tema Optionala

recapitulare BFS pe matrice ( LEE )

usor

mediu

greu

mai multe probleme [=47 aici]:

Tema Rezolvari

LantMinim

Lant minim de la p la q - afisare drum Se dă lista muchiilor unui graf neorientat și două vârfuri p q. Să se determine cel mai scurt lanț cu extremitățile p q.

lantminim.in
6 2 6
1 2
1 3
1 4
3 4
4 5
4 6
5 6
lantminim.out
4
2 1 4 6 

Observatie: Lungimea lantului intre 2 1 4 6 este de fapt 3. Prin definitie, lungimea unui lant este numarul muchiilor, nu numarul de noduri componente ale lantului. Problema specifica insa ca se doreste a se afisa numarul de noduri din lantul minim, ci nu lungimea lantului.

Rezolvare: Vom parcurge in latime graful, pornind de la nodul p. Pe masura ce vom vizita nodurile:

  • vom memora pentru fiecare nod distanta de la p la acestea, intr-un vector dist. Astfel ca la final dist[q] va retine distanta de la p la q.
  • vom memora pentru fiecare nod, nodul prin care s-a ajuns la acesta, adica predecesorul lui, sau cum l-am denumit aici tatal. Acest vector de tati ne ajuta sa reconstituim drumul din aproape in aproape pornind de la nodul q, pana la nodul de pornire, pentru care tatal e 0.


#include <fstream>

using namespace std;
ifstream fin("lantminim.in");
ofstream fout("lantminim.out");
/// pt p = 2 si q = 6 si muchiile : 1 2, 1 3, 1 4,3 4,4 5,4 6,5 6
/// nod:  1 2 3 4 5 6 7
/// tati: 2 0   1   4     // tatal lui 6 e 4, al lui 4 e 1, al lui 1 e 2, al lui 2 e 0 ( de la el am pornit deci ne oprim)

int n, m, i, j, p, q;
int a[101][101];
int coada[101];          // coada in care tin nodurile in ordinea vizitarii lor
int dist[101];           // dinst de la nodul de start pana la nodul curent
int tata[101];           // vector de frecventa in care marchez daca am vizitat un nod

void tipar( int q ){     // tipareste nodurile prin care am ajuns la q folosindu-ne de vect de tati
  if( q ){               // daca q e 0 ne oprim, 
    tipar ( tata[q] );   // tiparesc intai tatal lui q si apoi q 
    fout << q << " ";
  }
}
void BFS( int start ){                           // pornim parcurgerea de la un nod dat
  int k = 1;
  int vf = 0;

  dist[start] = 1;                               // marchez ca vizitat nodul start in vectorul in care itn distantele
  tata[start] = 0;
  coada[++vf] = start;                           // punem in coada nodul de la care pornim
  while( k <= vf ){                              // cata vreme mai am elemente in coada
    int nod = coada[k];                          // nodul curent
    //fout << nod << " ";                        // afisez nodul curent
    for(int i = 1; i <= n; i++ )                 // parcurg toti vecinii nodului curent
      if( a[nod][i] == 1 && dist[i] == 0 ){      // daca vecinii nu au fost vizitati (nu le-am marcat dist pana la p
        coada[++vf] = i;                         // ii pun in coada
        dist[i] = dist[nod] + 1;                 // dist de la start pana la el, e dist de la nod pana la start + 1
        tata[i] = nod;
      }
    k++;                                         // trec la urmatorul element din coada
  }
}

int main(){
  fin >> n >> p >> q;
  while( fin >> i >> j ){
    a[i][j] = a[j][i] = 1;
  }
  BFS( p );
  fout << dist[q] << "\n";   // tiparim dist de la p la q memorata in vect dist
  tipar( q );                // tiparim nodurile prin care am trecut ca sa ajung la q, folosindu-ne de vect de tati
  return 0;
}