Kose crte - Rešenje

Postavka

Prilično je jasno da se zadatak može rešiti tako što se prebroje komponente povezanosti u grafu koji gradimo na osnovu podataka o lavirintu, međutim, prvo treba na pravi način definisati taj graf. Jedan način je da svaki kvadrat u lavirintu podelimo na četiri oblasti i da svaka oblast predstavlja jedan čvor grafa (taj graf ima \(4mn\) čvorova).

Četiri oblasti u jednom kvadratu

Oblasti u jednom kvadratu su povezane, osim ako je postavljena neka živa ograda.

Mogući su prelasci i iz jednog u drugi kvadrat.

Na narednoj slici prikazana je povezanost oblasti u lavirintu koji se opisuje karakterima:

/\/ /

Odgovarajući graf je prikazan na narednoj slici.

Pošto kvadrata ima \(m \times n\), čvorova grafa ima \(4 \times m \times n\). Svaki čvor je povezan sa najviše \(3\) druga čvora, pa je ukupna složenost prebrojavanja komponenti \(O(mn)\).

// svakom polju odgovaraju ima 4 čvora grafa
enum deo {GORE=0, DOLE, LEVO, DESNO};

// obilazak grafa zadatog nizom stringova od cvora sa koordinatama (v0, k0, d0)
// to je cvor u vrsti v0, koloni k0 i delu d0
// poseceni cvorovi su određeni visedimenzionim nizom posecen
void dfs(const vector<string>& linije, int m, int n,
         vector<vector<vector<bool>>>& posecen,
         int v0, int k0, int d0) {
  // na steku cuvamo koordinate cvorova grafa
  stack<tuple<int, int, int>> s;
  s.emplace(v0, k0, d0);
  posecen[v0][k0][d0] = true;
    
  while (!s.empty()) {
    // skidamo koordinate tekuceg cvora sa steka
    int v, k, d;
    tie(v, k, d) = s.top(); s.pop();

    // odredjujemo susede tekuceg cvora (ima ih najvise 3)
    vector<tuple<int, int, int>> susedi;
    // analiziramo polozaj tekuceg cvora u njegovom polju
    switch(d) {
    case GORE:
      // levi i desni cvor tekuceg polja (ako nisu zaklonjeni preprekama)
      if (linije[v][k] != '\\')
        susedi.emplace_back(v, k, LEVO);
      if (linije[v][k] != '/')
        susedi.emplace_back(v, k, DESNO);
      // donji cvor polja iznad (ako to polje postoji)
      if (v > 0)
        susedi.emplace_back(v-1, k, DOLE);
      break;
    case DOLE:
      // levi i desni cvor tekuceg polja (ako nisu zaklonjeni preprekama)
      if (linije[v][k] != '/')
        susedi.emplace_back(v, k, LEVO);
      if (linije[v][k] != '\\')
        susedi.emplace_back(v, k, DESNO);
      // gornji cvor polja ispod (ako to polje postoji)
      if (v < m-1)
        susedi.emplace_back(v+1, k, GORE);
      break;
      // ...
    }

    // prolazimo kroz niz suseda tekuceg cvora
    for (const auto& t : susedi) {
      int vv, kk, dd;
      tie(vv, kk, dd) = t;
      // neposecene susede stavljamo na stek
      if (!posecen[vv][kk][dd]) {
        posecen[vv][kk][dd] = true;
        s.push(t);
      }
    }
  }
}

int main() {
  // ucitavamo crtez lavirinta
  int m, n;
  cin >> m >> n;
  string s;
  getline(cin, s);
  vector<string> linije(m);
  string linija;
  for (int i = 0; i < m; i++)
    getline(cin, linije[i]);

  // alociramo vektor posecenosti cvorova
  // na pocetku nije posecen ni jedan cvor 
  vector<vector<vector<bool>>> posecen;
  posecen.resize(m);
  for (int i = 0; i < m; i++) {
    posecen[i].resize(n);
    for (int j = 0; j < n; j++)
      posecen[i][j].resize(4, false);
  }

  // odredjujemo komponente povezanosti grafa i ispisujemo njihov broj
  int brojOblasti = 0;
  for (int v = 0; v < m; v++)
    for (int k = 0; k < n; k++)
      for (int d = GORE; d <= DESNO; d++)
        if (!posecen[v][k][d]) {
          dfs(linije, m, n, posecen, v, k, d);
          brojOblasti++;
        }


  cout << brojOblasti << endl;
  return 0;
}
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <tuple>

using namespace std;

// svakom polju odgovaraju ima 4 čvora grafa
enum deo {GORE=0, DOLE, LEVO, DESNO};

// obilazak grafa zadatog nizom stringova od cvora sa koordinatama (v0, k0, d0)
// to je cvor u vrsti v0, koloni k0 i delu d0
// poseceni cvorovi su određeni visedimenzionim nizom posecen
void dfs(const vector<string>& linije, int m, int n,
         vector<vector<vector<bool>>>& posecen,
         int v0, int k0, int d0) {
  // na steku cuvamo koordinate cvorova grafa
  stack<tuple<int, int, int>> s;
  s.emplace(v0, k0, d0);
  posecen[v0][k0][d0] = true;
    
  while (!s.empty()) {
    // skidamo koordinate tekuceg cvora sa steka
    int v, k, d;
    tie(v, k, d) = s.top(); s.pop();

    // odredjujemo susede tekuceg cvora (ima ih najvise 3)
    vector<tuple<int, int, int>> susedi;
    // analiziramo polozaj tekuceg cvora u njegovom polju
    switch(d) {
    case GORE:
      // levi i desni cvor tekuceg polja (ako nisu zaklonjeni preprekama)
      if (linije[v][k] != '\\')
        susedi.emplace_back(v, k, LEVO);
      if (linije[v][k] != '/')
        susedi.emplace_back(v, k, DESNO);
      // donji cvor polja iznad (ako to polje postoji)
      if (v > 0)
        susedi.emplace_back(v-1, k, DOLE);
      break;
    case DOLE:
      // levi i desni cvor tekuceg polja (ako nisu zaklonjeni preprekama)
      if (linije[v][k] != '/')
        susedi.emplace_back(v, k, LEVO);
      if (linije[v][k] != '\\')
        susedi.emplace_back(v, k, DESNO);
      // gornji cvor polja ispod (ako to polje postoji)
      if (v < m-1)
        susedi.emplace_back(v+1, k, GORE);
      break;
    case LEVO:
      // donji i gornji cvor tekuceg polja (ako nisu zaklonjeni preprekama) 
      if (linije[v][k] != '/')
        susedi.emplace_back(v, k, DOLE);
      if (linije[v][k] != '\\')
        susedi.emplace_back(v, k, GORE);
      // desni cvor polja levo (ako to polje postoji)
      if (k > 0)
        susedi.emplace_back(v, k-1, DESNO);
      break;
    case DESNO:
      // donji i gornji cvor tekuceg polja (ako nisu zaklonjeni preprekama)
      if (linije[v][k] != '\\')
        susedi.emplace_back(v, k, DOLE);
      if (linije[v][k] != '/')
        susedi.emplace_back(v, k, GORE);
      // levi cvor polja desno (ako to polje postoji)
      if (k < n-1)
        susedi.emplace_back(v, k+1, LEVO);
      break;
    }

    // prolazimo kroz niz suseda tekuceg cvora
    for (const auto& t : susedi) {
      int vv, kk, dd;
      tie(vv, kk, dd) = t;
      // neposecene susede stavljamo na stek
      if (!posecen[vv][kk][dd]) {
        posecen[vv][kk][dd] = true;
        s.push(t);
      }
    }
  }
}

int main() {
  // ucitavamo crtez lavirinta
  int m, n;
  cin >> m >> n;
  string s;
  getline(cin, s);
  vector<string> linije(m);
  string linija;
  for (int i = 0; i < m; i++)
    getline(cin, linije[i]);

  // alociramo vektor posecenosti cvorova
  // na pocetku nije posecen ni jedan cvor 
  vector<vector<vector<bool>>> posecen;
  posecen.resize(m);
  for (int i = 0; i < m; i++) {
    posecen[i].resize(n);
    for (int j = 0; j < n; j++)
      posecen[i][j].resize(4, false);
  }

  // odredjujemo komponente povezanosti grafa i ispisujemo njihov broj
  int brojOblasti = 0;
  for (int v = 0; v < m; v++)
    for (int k = 0; k < n; k++)
      for (int d = GORE; d <= DESNO; d++)
        if (!posecen[v][k][d]) {
          dfs(linije, m, n, posecen, v, k, d);
          brojOblasti++;
        }


  cout << brojOblasti << endl;
  return 0;
}