본문 바로가기
알고리즘&자료구조/BFS

백준 7576

by do_ng 2024. 4. 20.

1. 문제이해

 

 

2. 문제분석

(1,1) ~ (M, N) 까지 이중 for문을 돌면서 1(토마토가 익은 지점)이 있으면 그 지점을 기준으로 삼아서 주변에 0(익지 않은 토마토)를 1로 만드는 과정을 수행하면 되는데 익은 토마토로 만드는 과정이 최대 N*M번 수행될 수 있으니 O(N*N*M*M)의 시간복잡도가 걸리게 되어서 시간 내로 해결이 불가능하다.

예시를 들자면 n=2, m=3 일때 토마토가 익은지점까지 찾아가는데 4번(n*m)이 걸리고 토마토가 익은지점에서 인접한 익지 않은 토마토를 큐에 push, pop 해서 모두 익은 토마토로 만드는 과정이 4번(n*m)걸린다.

 

그래서 1(토마토가 익은)인 지점을 모두 큐에 넣고 BFS를 수행하는 것이다.

그리고 board 배열(익은 토마토, 익지않은 토마토, 벽)과 dist 배열(익은 토마토로 구성된 곳)을 만들어서 구분시킨다.

board에서 1인 지점을 pop할 때 dist 배열에서 상하좌우로 익지않은 토마토(-1)를 체크하는데 0(벽)이 있으므로 큐에 push할 데이터가 없게 되므로 큐는 종료된다. 

dist 배열에서 0을 벽으로 설정한 이유는 Int의 초기값인 0을 사용했고 토마토가 익는 횟수가 증가할수록 0보다 커지기 때문에 익지 않은 토마토는 -1로 설정했다. 

(상하좌우로 익지 않은 토마토를 확인할 때 -1인 것만 체크하기 위해서, 0 또는 1이상이면 벽이거나 토마토가 익는 최소횟수이기 때문)

 

"이러한 문제 풀이 방법을 어떻게 생각해냈을까?

처음에는 생각하기 힘들겠지만 응용된 문제풀이 방법을 계속 습득하다 보면 비슷한 응용문제도 풀 수 있을꺼라 생각함"

 

 

3. 코드

#include <iostream>
#include <utility>
#include <queue>
using namespace std;
#define X first
#define Y second 

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

int board[1002][1002];
int dist[1002][1002];

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);    

    // 1.입력
    int m, n; // m(행:y), n(열:x)
    cin >> m >> n;
    queue<pair<int, int>> Q;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> board[i][j];
            if (board[i][j] == 1) {
                Q.push({ i,j });
            }
            if (board[i][j] == 0) {
                dist[i][j] = -1;
            }
        }
    }    

    // 2.로직
    // 1인 지점을 모두 큐에 넣어줌
    // 0(토마토가 익지 않음)인 지점은 dist 배열에 값을 -1로 변경
    // 왜냐하면 1인 지점이랑 인접한 0은 모두 1이상으로 바뀌기 때문에 모든 토마토를 익게 만들었을 때 익지 못한 토마토를 구별하기 위해서 
    while (!Q.empty()) {
        pair<int, int> cur = Q.front(); Q.pop();
        for (int dir = 0; dir < 4; dir++) {
            int nx = cur.X + dx[dir];
            int ny = cur.Y + dy[dir];
            if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
            if (dist[nx][ny] >= 0) continue; // -1이면 모두 0(토마토가 익지 않음)이기 때문에 조건이 성립하지 않음
            dist[nx][ny] = dist[cur.X][cur.Y] + 1; // 해당 익지 않은 토마토까지 도달한 횟수
            Q.push({ nx, ny }); // 익지않은 토마토가 익은 토마토로 변경된것을 큐에 넣음
        }
    }

    int minDays = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (dist[i][j] == -1) {
                cout << -1;
                return 0;
            }
            if (dist[i][j] > minDays) {
                minDays = dist[i][j];
            }
        }
    }

    // 3.결과
    cout << minDays;

    

    return 0;
}

 

 

재풀이

 

 

#include<iostream>	
#include<queue>
using namespace std;

int board[1002][1002];
int vis[1002][1002];
int dx[] = { -1,1,0,0 };
int dy[] = { 0,0,-1,1 };
int m, n;

int main(void) {
	ios_base::sync_with_stdio(0);
	cin.tie(0);
	
	// 1.입력
	cin >> m >> n; // m(가로), n(세로)
	queue<pair<int, int>> Q;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			cin >> board[i][j];
			// 익은 토마토(1)만 큐에 넣음
			if (board[i][j] == 1) {
				vis[i][j] = 1;
				Q.push({ i,j });
			}			
		}
	}

	// 2.로직
	int minDay = 0;
	while (!Q.empty()) {
		pair<int, int> cur = Q.front();
		Q.pop();

		for (int dir = 0; dir < 4; dir++) {
			int nx = cur.first + dx[dir];
			int ny = cur.second + dy[dir];

			if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
			if (board[nx][ny] != 0) continue;
			board[nx][ny] = 1;
			vis[nx][ny] = vis[cur.first][cur.second] + 1;
			minDay = vis[nx][ny]; // 여기 부분이 비효율적인데 만약에 인접한 4곳 모두 익지 않은 토마토인 경우 4번의 연산이 발생
			Q.push({ nx, ny });
		}
	}
	
	// 토마토가 모두 익지 못하는 상태
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			if (board[i][j] == 0) {
				cout << -1;
				return 0;
			}
		}
	}

	// 토마토가 이미 모두 익어있는 상태
	if (minDay == 0) {
		cout << 0;
	}
	else { // 토마토가 모두 익기까지의 최초일수
		cout << minDay - 1;
	}	
		
	return 0;
}

'알고리즘&자료구조 > BFS' 카테고리의 다른 글

백준 1697  (1) 2024.04.23
백준 4179  (2) 2024.04.21
백준 2178  (1) 2024.04.18
BFS(Breadth First Search)  (2) 2024.04.18
DFS(Depth First Search)  (0) 2024.02.19