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

백준 5427

by do_ng 2024. 5. 1.

1. 문제이해
매초마다 불은 상하좌우 빈공간으로 이동
상근이도 매초마다 상하좌우로 이동 가능 벽쪽으로 이동 X, 불이 있는 칸 또는 이제 불이 붙으려는 칸으로 이동 X
빌딩을 탈출하기 위한 최소 시간을 구하기

2. 문제분석

지도(board), 불이동시간(fireMoveT), 상근이 이동시간(playerMoveT) 총 3개의 배열 필요
불이붙는 이동시간을 계산할때 불이 최초에 있는 지점은 0초이고 0이상인 경우 불이 있는 곳이기 때문에 체크 X

상근이의 이동시간(PT)이 불이 붙는시간(FT)보다 무조건 작아야지 이동가능

- 지도에 불과 상근이의 위치를 나타내면서 불이 있는 모든 위치를 큐에 넣음
- 큐를 통해서 지도 전체에 불이 붙는 시간을 모두 계산함

- 상근이의 시작위치를 큐에 넣음
- 상근이가 이동하면서 FT와 비교하면서 이동가능한 거리 계산
- map을 빠져나가는 경우 최소시간 출력

 

3. 코드

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

char board[1001][1001];
int fireMoveT[1001][1001];
int playerMoveT[1001][1001];

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

int main(void) {
	ios_base::sync_with_stdio(0);
	cin.tie(0);

	// 1.입력
	int T;
	cin >> T;

	while (T--) {
		int w, h; // w(열), h(행)
		cin >> w >> h;

		// fireMoveT, playerMoveT 초기화
		for (int i = 0; i < h; i++) {
			for (int j = 0; j < w; j++) {
				fireMoveT[i][j] = -1;
				playerMoveT[i][j] = -1;
			}
		}

		queue<pair<int, int>> Q1; // 불 위치
		queue<pair<int, int>> Q2; // 상근이 위치
		for (int i = 0; i < h; i++) {
			for (int j = 0; j < w; j++) {
				cin >> board[i][j];				
				if (board[i][j] == '*') {
					fireMoveT[i][j] = 0;
					Q1.push({ i,j });
				} else if (board[i][j] == '@') {
					playerMoveT[i][j] = 0;
					Q2.push({ i,j });
				}
			}
		}		

		// 2.로직
		// 상근이가 처음부터 가장자리에 있는 경우
		pair<int, int> start = Q2.front();
		if (start.first == 0 || start.first == h - 1 || start.second == 0 || start.second == w - 1) {
			cout << 1 << '\n';
			continue;
		}

		// 지도 전체에 불이 붙는 시간 
		while (!Q1.empty()) {
			pair<int, int> cur = Q1.front();
			Q1.pop();
			for (int dir = 0; dir < 4; dir++) {
				int nx = cur.first + dx[dir];
				int ny = cur.second + dy[dir];
				if (nx < 0 || nx >= h || ny < 0 || ny >= w) continue;
				if (board[nx][ny] == '#' || fireMoveT[nx][ny] >= 0) continue;
				fireMoveT[nx][ny] = fireMoveT[cur.first][cur.second] + 1;
				Q1.push({ nx, ny });
			}
		}

		// 상근이의 이동시간
		bool notMove = false;
		while ( (!Q2.empty()) && (!notMove) ) {
			pair<int, int> cur = Q2.front();
			Q2.pop();
			for (int dir = 0; dir < 4; dir++) {
				int nx = cur.first + dx[dir];
				int ny = cur.second + dy[dir];

				if (nx < 0 || nx >= h || ny < 0 || ny >= w) continue;
				if (board[nx][ny] == '#') continue;
				if (playerMoveT[nx][ny] >= 0) continue;
				// 상근이의 이동시간이 불의 이동시간보다 무조건 작아야지 상근이가 이동이 가능 (이동시간이 동일하다면 동시에 접근하는 경우이기 때문에 이동 X)
				// 단, 불의 초기값을 -1로 세팅해놓았기 때문에 불이 없는 경우 -1보다 작은 값이 나오지 않기 때문에 이동이 불가능하므로 -1은 제외한다
				if (fireMoveT[nx][ny] != -1 && playerMoveT[cur.first][cur.second] + 1 >= fireMoveT[nx][ny]) continue;

				// 가장자리에 있는 경우 다음 for문에서 탈출하니까 여기서 이동시간 체크
				// **상근이가 처음부터 가장자리에 있는 경우는 이 식이 실행이 안됨**
				if (nx == 0 || nx == h-1 || ny == 0 || ny == w-1) {
					cout << playerMoveT[cur.first][cur.second] + 2 << "\n";
					notMove = true;
					break;
				}
				
				playerMoveT[nx][ny] = playerMoveT[cur.first][cur.second] + 1;
				Q2.push({ nx, ny });
			}
		}

		if (notMove == false) cout << "IMPOSSIBLE" << "\n";
	}


	return 0;
}

 

풀이방법은 생각해냈지만 구현단계에서 많은 어려움을 겪음

생각하지도 못한 반례들에서 실패함 난이도는.. 지금까지 풀었던 BFS중에 가장 어려웠음

 

 

재풀이 코드

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

char board[1001][1001];
int fireDis[1001][1001];
int playerDis[1001][1001];
int dx[4] = { -1,1,0,0 };
int dy[4] = { 0,0,-1,1 };
int w, h;


int main(void) {
	ios_base::sync_with_stdio(0);
	cin.tie(0);
	
	int t;
	cin >> t;
	while (t--) {
		cin >> w >> h;
		queue<pair<int, int>> Q1; // 불 이동 BFS
		queue<pair<int, int>> Q2; // 상근 이동 BFS
		for (int i = 0; i < h; i++) {
			string str;
			cin >> str;
			for (int j = 0; j < w; j++) {
				board[i][j] = str[j];
				// 불 또는 상근이가 있는 첫 위치를 0초로 시작하기 때문에 방문하지 않은 칸은 -1로 설정
				fireDis[i][j] = -1;
				playerDis[i][j] = -1;
				if (str[j] == '*') {
					fireDis[i][j] = 0;
					Q1.push({ i,j });
				}
				else if (str[j] == '@') {
					playerDis[i][j] = 0;
					Q2.push({ i,j });
				}
			}
		}

		// 1.불이 붙는 모든 시간을 계산
		while (!Q1.empty()) {
			pair<int, int> cur = Q1.front(); Q1.pop();
			for (int dir = 0; dir < 4; dir++) {
				int nx = cur.first + dx[dir];
				int ny = cur.second + dy[dir];
				if (nx < 0 || nx >= h || ny < 0 || ny >= w) continue;
				if (board[nx][ny] == '#' || fireDis[nx][ny] >= 0)continue;
				fireDis[nx][ny] = fireDis[cur.first][cur.second] + 1;
				Q1.push({ nx, ny });
			}
		}

		// 2.상근이의 이동시간 계산
		bool exitCheck = false;
		while (!Q2.empty() && (!exitCheck)) {
			pair<int, int> cur = Q2.front(); Q2.pop();
			for (int dir = 0; dir < 4; dir++) {
				int nx = cur.first + dx[dir];
				int ny = cur.second + dy[dir];

				// 가장자리에서 한칸 더 이동하는 경우 board의 범위를 빠져나가기 때문에 탈출로 판단
				if (nx < 0 || ny < 0 || nx >= h || ny >= w) {
					cout << playerDis[cur.first][cur.second] + 1 << '\n';
					exitCheck = true;
					break;
				}
				
				if (fireDis[nx][ny] != -1 && playerDis[cur.first][cur.second] + 1 >= fireDis[nx][ny]) continue;
				if (board[nx][ny] == '#' || playerDis[nx][ny] >= 0) continue;
				playerDis[nx][ny] = playerDis[cur.first][cur.second] + 1;
				
				Q2.push({ nx, ny });
			}
		}

		if (!exitCheck) cout << "IMPOSSIBLE" << '\n';
	}		
		
	return 0;
}

 

2달 전에 풀었던 문제지만, 해설지를 참고해서 품

어느 정도 구현방법은 맞았지만 디테일한 예외부분에 대해서 생각하지 못함

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

프로그래머스 - 게임 맵 최단거리  (0) 2024.06.05
백준 7562  (0) 2024.04.30
백준 7569  (1) 2024.04.28
백준 10026  (2) 2024.04.27
백준 1012  (2) 2024.04.26