μ˜€λŠ˜μ€ λ°±μ€€ 7576번 문제, ν† λ§ˆν† λ₯Ό κ°€μ Έμ™”μŠ΅λ‹ˆλ‹€
문제 λ‚΄μš©μ€ νŒŒμ•…ν–ˆλ‹€κ³  κ°€μ •ν•˜κ³  제 μ‚¬κ³ μ˜ 흐름을 λ°”λ‘œ μ„€λͺ…ν•΄λ³Όκ²Œμš”

ν† λ§ˆν† κ°€ 1개라고 κ°€μ •ν•΄λ³ΌκΉŒμš” ?
이 λ¬Έμ œλŠ” ν•œ μ μ—μ„œ νΌμ Έλ‚˜κ°€ λͺ¨λ“  ν† λ§ˆν† μ„ λ°©λ¬Έν•˜κ²Œ ν•˜λŠ” 데 β€˜μ΅œμ†Œβ€™ λͺ‡μΌμ΄ μ§€λ‚˜μ•Όν•˜λŠ”μ§€ 묻고 μžˆμŠ΅λ‹ˆλ‹€
β€˜κ·Έλž˜ν”„μ˜ μ΅œλ‹¨κ±°λ¦¬β€™ λ¬Έμ œμ™€ μƒλ‹Ήνžˆ μœ μ‚¬ν•΄λ³΄μž…λ‹ˆλ‹€

'κ·Έλž˜ν”„ μ΅œλ‹¨κ±°λ¦¬'λΌλŠ” 어ꡬ가 μž…λ ₯ x둜 μ£Όμ–΄μ§€λ©΄ μ €λŠ” λ°˜μ‚¬μ μœΌλ‘œ 'BFS'λΌλŠ” 좜λ ₯ yλ₯Ό λ– μ˜¬λ¦¬λŠ”λ°μš”
κ΅¬μ’…λ§Œμ„ μƒλ‹˜μ˜ μ±… μ•Œκ³ λ¦¬μ¦˜ λ¬Έμ œν•΄κ²°μ „λž΅μ—μ„œλŠ” DFS와 달리 BFSλŠ” κ·Έλž˜ν”„ μƒμ—μ„œ β€˜μ΅œλ‹¨κ±°λ¦¬β€™λ₯Ό μ°ΎλŠ” μš©λ„λ‘œ μ œν•œλœλ‹€κ³  ν–ˆμ—ˆμŠ΅λ‹ˆλ‹€ 이에 λ°˜ν•΄μ„œ DFSλŠ” κ·Έλž˜ν”„μ˜ 고유 νŠΉμ§•μ„ μΆ”μΆœν•˜λŠ” 데 μœ μš©ν•΄ μ‚¬μš© λ²”μœ„κ°€ λ„“λ‹€κ³  ν–ˆμ£ 

μ•„λž˜ μ½”λ“œλŠ” BFS κΈ°λ³Έ κ³¨κ²©μž…λ‹ˆλ‹€ (예제 μ†ŒμŠ€μ½”λ“œλŠ” c++둜 μž‘μ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€)

void bfs(int start, const vector<vector<int>>& graph, vector<bool>& visited) {
    queue<int> q;
    q.push(start);
    visited[start] = true;

    while (!q.empty()) {
        int curr = q.front();
        q.pop();
        cout << curr << " "; // λ°©λ¬Έν•œ λ…Έλ“œ 좜λ ₯

        for (int next : graph[curr]) {
            if (!visited[next]) {
                q.push(next);
                visited[next] = true;
            }
        }
    }
}

μœ„ μ½”λ“œλŠ” 2차원 맀트릭슀 graph둜 μΈμ ‘λ¦¬μŠ€νŠΈλ₯Ό κ΅¬ν˜„ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€
bfs κ΅¬ν˜„μ˜ 핡심은 자료ꡬ쑰 queue둜, 큐λ₯Ό 톡해 λ¨Όμ € νˆ¬μž…λœ λ…Έλ“œλ₯Ό λ¨Όμ € λ°©λ¬Έν•˜κ³ , 이둜써 κΉŠμ΄κ°€ 얕은 λ…Έλ“œλ“€λΆ€ν„° λ°©λ¬Έν•  수 있게 λ©λ‹ˆλ‹€
또 λ…Έλ“œ λ°©λ¬Έμ—¬λΆ€λ₯Ό λ‚˜νƒ€λ‚΄λŠ” 벑터 visitedλ₯Ό 톡해 λ…Έλ“œλ₯Ό μ€‘λ³΅ν•΄μ„œ λ°©λ¬Έν•˜μ§€ μ•Šκ²Œ ν•˜κ³ , while λ¬΄ν•œλ£¨ν”„λ₯Ό λ²—μ–΄λ‚  수 있게 ν•΄μ€λ‹ˆλ‹€
λ§€ ν„΄λ§ˆλ‹€ νμ—μ„œ λ…Έλ“œλ₯Ό κΊΌλ‚΄ ν•΄λ‹Ή λ…Έλ“œμ˜ μΈμ ‘λ…Έλ“œλ₯Ό 큐에 λͺ¨λ‘ 넣은 λ‹€μŒ, μΈμ ‘λ…Έλ“œλ“€μ€ λ°©λ¬Έν•œ μƒνƒœλ‘œ μ²˜λ¦¬ν•΄μ£Όκ³  μžˆμŠ΅λ‹ˆλ‹€ λ§€ ν„΄λ§ˆλ‹€ λ°©λ¬Έν•œ λ…Έλ“œλŠ” pop() ν•΄μ£ΌλŠ” κ±Έ μžŠμ§€ λ§ˆμ„Έμš”! (잊으면 λ¬΄ν•œλ£¨ν”„μ— λΉ μ§€κ²Œ λ©λ‹ˆλ‹€β€¦)

ν† λ§ˆν†  문제의 큰 틀은 μš°μ„  이 BFS μ†ŒμŠ€μ½”λ“œμΈλ°, λ³€μˆ˜κ°€ μžˆμŠ΅λ‹ˆλ‹€
초기 단계에 읡은 ν† λ§ˆν† κ°€ 2개라면 큐에 두 ν† λ§ˆν† λ‘œ 인해 읡은 ν† λ§ˆν† λ“€μ΄ λ“€μ–΄κ°€μ•Ό ν•©λ‹ˆλ‹€ 그런데 κΈ°μ‘΄ bfs와 달리 같은 날에 읡은 ν† λ§ˆν† (λ…Έλ“œ)듀은 ν•œλ²ˆμ— pop() ν•΄μ•Όν•©λ‹ˆλ‹€ λ¬Έμ œμ—μ„œ μš”κ΅¬ν•˜λŠ” 건 λͺ¨λ“  ν† λ§ˆν† κ°€ λ‹€ μ΅λŠ”λ° κ±Έλ¦¬λŠ” β€˜μ‹œκ°„β€™μ΄κΈ° λ•Œλ¬Έμ΄μ£  (λ‹€λ₯Έ 말둜, μ‹œκ°„μ˜ 흐름에 따라 읡은 ν† λ§ˆν† λ“€μ„ ꡬ뢄할 수 μžˆμ–΄μ•Ό ν•©λ‹ˆλ‹€)

μ œκ°€ 생각해낸 방법은 bfs의 큰 틀은 μœ μ§€ν•˜λ˜ 2개의 큐λ₯Ό μš΄μš©ν•˜λŠ” κ²ƒμ΄μ—ˆμŠ΅λ‹ˆλ‹€
λ‹€μŒ μ½”λ“œλŠ” μ‘μš©λœ bfs의 큰 ν‹€μž…λ‹ˆλ‹€

while(!q1.empty()) {
    while (!q1.empty()) {
        int curr = q1.front();
        q1.pop();

        for (int next : graph[curr]) {
            if (!visited[next]) {
                q2.push(next);
                visited[next] = true;
            }
        }
    }

    while (!q2.empty()) {
        int curr = q2.front();
        q2.pop();

        for (int next : graph[curr]) {
            if (!visited[next]) {
                q1.push(next);
                visited[next] = true;
            }
        }
    }
}

두 개의 νλŠ” λ‹€μŒκ³Ό 같이 μš΄μš©λ©λ‹ˆλ‹€
νƒ€μž„λΌμΈ 상 μž„μ˜μ˜ μ‹œμ  t에 λŒ€ν•΄ ν˜„μž¬ μ‹œμ  t에 λ°©λ¬Έν•  λ…Έλ“œλŠ” q1μ—μ„œ κΊΌλ‚΄μ§€κ³ , λ‹€μŒμ— λ°©λ¬Έν•΄μ•Ό ν•  λ…Έλ“œ(즉 t+1에 방문될 λ…Έλ“œμ΄μž q1μ—μ„œ κΊΌλ‚΄μ§„ λ…Έλ“œμ™€ μΈμ ‘ν•œ λ…Έλ“œλ“€)듀은 q2에 μΆ”κ°€λ©λ‹ˆλ‹€
이 λ§€μ»€λ‹ˆμ¦˜μ€ ν™€μˆ˜ μ‹œμ μ— λ°©λ¬Έν•  λ…Έλ“œμ™€ 짝수 μ‹œμ μ— λ°©λ¬Έν•  λ…Έλ“œλ₯Ό μ •ν™•νžˆ κ΅¬λΆ„ν•©λ‹ˆλ‹€

두 큐λ₯Ό μˆœνšŒν•˜λŠ” 두 while문을 큰 while문이 감싸고 μžˆμŠ΅λ‹ˆλ‹€ 이 λ°˜λ³΅λ¬Έμ€ 두 큐가 빈 μƒνƒœμΈμ§€ ν™•μΈν•˜λŠ”, 즉 탐색이 λλ‚¬λŠ”μ§€ νŒλ‹¨ν•˜λŠ” κΈ°μ‘΄ bfs μ½”λ“œμ˜ whileλ¬Έκ³Ό 같은 역할을 ν•©λ‹ˆλ‹€
이제 whileλ¬Έ 사이사이에 μ‹œκ°„ tλ₯Ό μ—…λ°μ΄νŠΈ ν•΄μ£Όκ³ (t++) λ‹€μŒκ³Ό 같은 쑰건듀을 μΆ”κ°€ν•΄μ£Όλ©΄ λ©λ‹ˆλ‹€

  • (읡은 ν† λ§ˆν†  수 + 빈자리)의 μˆ˜κ°€ 전체 λ°•μŠ€ μΉΈμˆ˜μ™€ λ™μΌν•œκ°€? -> 첫 μˆœνšŒμ‹œ 이렇닀면 μ²˜μŒλΆ€ν„° 읡힐 ν† λ§ˆν† κ°€ μ—†μœΌλ―€λ‘œ 반볡문 νƒˆμΆœ ν›„ -1 좜λ ₯, 그게 μ•„λ‹ˆλΌλ©΄ 탐색을 μ™„λ£Œν•œ κ²ƒμ΄λ‹ˆ 반볡문 νƒˆμΆœ
  • q1 순회 이후에도 μ—¬μ „νžˆ q2κ°€ λΉ„μ–΄μžˆλŠ”κ°€? ν˜Ήμ€ κ·Έ λ°˜λŒ€μΈκ°€? -> 더 이상 μˆœνšŒν•  칸이 μ—†μœΌλ―€λ‘œ 반볡문 νƒˆμΆœ


λ§ˆμ§€λ§‰μœΌλ‘œ 체슀판과 같이 νŠΉμˆ˜ν•œ ν˜•νƒœλ‘œ κ·Έλž˜ν”„λ₯Ό ν‘œν˜„ν•˜λŠ” μ½”λ“œμž…λ‹ˆλ‹€

int dx[4]={1,0,-1,0};
int dy[4]={0,-1,0,1};

// κ°€λ‘œ M, μ„Έλ‘œ N 크기의 맀트릭슀λ₯Ό 탐색
int nextn, nextm;
for(int i=0;i<4;++i) {
  nextn=nindex+dx[i];
  nextm=mindex+dy[i];
  if(nextn<0 || nextm<0 || nextn>=N || nextm>=M || box[nextn][nextm]==1) continue;
  if(box[nextn][nextm]==0) { q2.push({nextn, nextm}); }
}

체슀판, λ°”λ‘‘νŒμ€ ν”νžˆ 2차원 μ’Œν‘œλ‘œ λ‚˜νƒ€λ‚Ό 수 μžˆλŠ”λ°μš” 말의 이동에 μ œμ•½μ΄ μžˆλ‹€λ©΄ (예. μƒν•˜μ’Œμš°λ‘œλ§Œ 이동가λŠ₯, λŒ€κ°μ„  μ΄λ™λΆˆκ°€) μ’Œν‘œ μƒμ—μ„œ 증뢄을 λœ»ν•˜λŠ” dx,dy λ³€μˆ˜λ₯Ό μ΄μš©ν•΄ 보닀 μ‰½κ²Œ 탐색을 κ΅¬ν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€
μ΄λ•Œ μ·¨ν•  수 μžˆλŠ” μ„ νƒμ§€λŠ” 4κ°€μ§€μ΄λ―€λ‘œ dx, dy μ—­μ‹œ 크기 4의 λ°°μ—΄λ‘œ ν‘œν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€
2차원 λ²‘ν„°λ‘œ κ·Έλž˜ν”„λ₯Ό ν‘œν˜„ν•˜λ©΄ μΈμ ‘λ¦¬μŠ€νŠΈ λ˜λŠ” 인접행렬이 λΆˆν•„μš”ν•˜λ‹€λŠ” μž₯점이 μžˆμŠ΅λ‹ˆλ‹€


전체 μ†ŒμŠ€μ½”λ“œ

#include <iostream>
#include <vector>
#include <algorithm>
#include <utility>
#include <queue>
using namespace std;
#define fastio ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);

int dx[4]={1,0,-1,0};
int dy[4]={0,-1,0,1};

int main() {
  fastio;
  queue<pair<int,int>> q1,q2;
  int days=0;
  int rotton=0, non=0;
  int N,M;
  cin >> M >> N;
  int numBox=M*N;
  vector<vector<int>> box(N,vector<int>(M,-1));
  for(int n=0;n<N;++n) {
    for(int m=0;m<M;++m) {
      cin >> box[n][m];
      if(box[n][m]==1) { rotton++; q1.push({n,m}); }
      if(box[n][m]==-1) non++;
    }
  }

  if(rotton+non==numBox) { cout << 0 << '\n'; return 0; }
  while(!q1.empty()) {
    while(!q1.empty()) {
      int nindex=q1.front().first; int mindex=q1.front().second; q1.pop();
      int nextn, nextm;
      for(int i=0;i<4;++i) {
        nextn=nindex+dx[i];
        nextm=mindex+dy[i];
        if(nextn<0 || nextm<0 || nextn>=N || nextm>=M || box[nextn][nextm]==1) continue;
        if(box[nextn][nextm]==0) { box[nextn][nextm]=1; q2.push({nextn, nextm}); rotton++; }
      }
      if(rotton+non==numBox) { cout << days+1 << '\n'; return 0; }
    }
    days++;
    if(q2.empty()) break;
    while(!q2.empty()) {
      int nindex=q2.front().first; int mindex=q2.front().second; q2.pop();
      int nextn, nextm;
      for(int i=0;i<4;++i) {
        nextn=nindex+dx[i];
        nextm=mindex+dy[i];
        if(nextn<0 || nextm<0 || nextn>=N || nextm>=M || box[nextn][nextm]==1) continue;
        if(box[nextn][nextm]==0) { box[nextn][nextm]=1; q1.push({nextn, nextm}); rotton++; }
      }
      if(rotton+non==numBox) { cout << days+1 << '\n'; return 0; }
    }
    days++;
  }
  if(rotton+non != numBox) { cout << -1 << '\n'; return 0; }
  cout << days << '\n';
  return 0;
}