문제 링크


https://www.acmicpc.net/problem/2206

 

2206번: 벽 부수고 이동하기

N×M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 당신은 (1, 1)에서 (N, M)의 위치까지 이동하려 하는데, 이때 최단 경로��

www.acmicpc.net

 

접근 방법


 문제를 읽어보면 BFS알고리즘으로 풀어야된다는 직감은 확실히 들것이다. 하지만 벽을 부수는 것이 가능한 기회가 단 한번 주어지는대, 이로 인해 풀이에 있어 접근이 쉽지 않을 것이다.

 일단 문제를 보자마자 떠올릴 수 있는 방법이 있는데 map에서 모든 벽들을 한번씩 0으로 바꿔 보고 BFS를 해보는 것이다. 하지만 이렇게 하는 것은 굉장히 비효율적이고 시간안에 해결 할 수 없다. 

 문제 풀이에 있어 핵심은 어떤 특정한 지점에 "벽을 한번 부수고 난 이후에 도착 한 시간" 과 "벽을 한번도 부수지 않은 상태에서 도착한 시간"이 다르다는 것이다. 이것을 이해했다면 문제를 풀어보자.

 

(1) 변수

   isbreak는 벽을 부순적이 없다면 false 부순적이 있다면 true이다.

그렇다면 visited[i][j][ isbreak=true ]i, j위치에 벽을 부수고 나서 도착했을때의 시간이고,

visited[i][j][ isbreak=false ]벽을 부수지 않고 도착했을때의 시간이 된다. 

 

(2) 풀이

  전반적인 부분이 다른 BFS문제들과 매우 비슷하므로, 가장 핵심이 되는 부분만 설명하겠다.

if (!isbreak && map[next_x][next_y] == 1) {
	//벽을 부순적이 없는데 가로막힌 길이라면
    visited[next_x][next_y][true] = visited[x][y][false] + 1;
    q.push({ next_x, next_y, true });
}
else if (map[next_x][next_y] == 0 && visited[next_x][next_y][isbreak] == 0) { 
    //지나갈 수 있는 길이고, 방문한적이 없다면
    visited[next_x][next_y][isbreak] = visited[x][y][isbreak] + 1;
    q.push({ next_x, next_y, isbreak });
}

  첫 번째 if문은 isbreak = false이고 map[next_x][next_y] = 1 인 조건에서 진입이 가능한데, 이를 쉽게 풀어쓰면

"next_x, next_y의 좌표에 도달할 때 까지 벽을 부순적이 없는데 벽이 있다면" 진입을 한다는 것이다.

진입을 했다면 이제 벽을 부수고 해당지점에 도착했다는 것을 나타내주면 된다.

  두 번째 if문은 "일단 지나갈 수 있는 길이고, 방문한 적이 없다면" 방문을 하여 시간을 기록하고 큐에 push하면 된다. 이는 너무나도 당연한 것이기에 자세한 설명은 하지 않겠다.

 

  bfs( )함수의 while문 안에서 좌표가 n,m이면 도달한 시간을 return하게 된다. 그 말은 즉 while()안에서 return을 하지 못한다면 map[n][m]에 도달하지 못했다는 의미가 되므로 -1을 return한다. 

소스 코드


#include <iostream>
#include <queue>

using namespace std;

typedef struct info {
	int x;
	int y;
	bool isbreak;
};

int map[1001][1001];
int visited[1001][1001][2]; //같은 지점이여도 벽을 부수고 도착했을때와 벽을 부수지 않고 도착했을 때 값이 달라지기 때문
int n, m;

int bfs() {

	queue <info> q;
	visited[1][1][false] = 1;
	q.push({ 1,1,false }); //시작 지점 , 벽을 아직 부수지 않았음

	while (!q.empty()) {

		int x = q.front().x;
		int y = q.front().y;
		bool isbreak = q.front().isbreak;
		q.pop();

		if (x == n && y == m) //목표지점에 도착
			return visited[x][y][isbreak];

		pair<int, int> direction[4] = { {1,0}, {-1,0} ,{0,1} ,{0,-1} };

		for (int i = 0; i < 4; i++) {

			int next_x = x + direction[i].first;
			int next_y = y + direction[i].second;

			if (!(next_x < 1 || n < next_x || next_y < 1 || m < next_y)) { //다음 좌표가 map에 포함되어 있다면
				if (!isbreak && map[next_x][next_y] == 1) {
					//벽을 부순적이 없는데 가로막힌 길이라면
					visited[next_x][next_y][true] = visited[x][y][false] + 1;
					q.push({ next_x, next_y, true });
				}
				else if (map[next_x][next_y] == 0 && visited[next_x][next_y][isbreak] == 0) { 
					//지나갈 수 있는 길이고, 방문한적이 없다면
					visited[next_x][next_y][isbreak] = visited[x][y][isbreak] + 1;
					q.push({ next_x, next_y, isbreak });
				}
			}
		}
	}
	return -1;
}

int main() {

	cin >> n >> m;

	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			scanf("%1d", &map[i][j]);

	cout << bfs();
}

개발 환경 : VS 2019

질문, 지적 환영입니다.

'알고리즘 > 백준' 카테고리의 다른 글

[C++] 백준 15683 감시  (0) 2020.05.20
[C++] 백준 14500 테트로미노  (0) 2020.05.20
[C++] 백준 1697 숨바꼭질  (0) 2020.05.19
[C++] 백준 14501 퇴사  (0) 2020.05.19
[C++] 백준 12865 평범한 배낭  (2) 2020.05.18

+ Recent posts