본문 바로가기

Algorithm/코드 풀이

2206번: 벽 부수고 이동하기

문제 설명

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

 

2206번: 벽 부수고 이동하기

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

www.acmicpc.net

[문제]

 N×M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 당신은 (1, 1)에서 (N, M)의 위치까지 이동하려 하는데, 이때 최단 경로로 이동하려 한다. 최단경로는 맵에서 가장 적은 개수의 칸을 지나는 경로를 말하는데, 이때 시작하는 칸과 끝나는 칸도 포함해서 센다.

 만약에 이동하는 도중에 한 개의 벽을 부수고 이동하는 것이 좀 더 경로가 짧아진다면, 벽을 한 개 까지 부수고 이동하여도 된다.

 한 칸에서 이동할 수 있는 칸은 상하좌우로 인접한 칸이다.

 맵이 주어졌을 때, 최단 경로를 구해 내는 프로그램을 작성하시오.

 

[입력]

 첫째 줄에 N(1 ≤ N ≤ 1,000), M(1 ≤ M ≤ 1,000)이 주어진다. 다음 N개의 줄에 M개의 숫자로 맵이 주어진다. (1, 1)과 (N, M)은 항상 0이라고 가정하자.

 

[출력]

 첫째 줄에 최단 거리를 출력한다. 불가능할 때는 -1을 출력한다.

 

[예제 입력 1]

6 4
0100
1110
1000
0000
0111
0000

[예제 출력 1]

15

 

[예제 입력 2]

4 4
0111
1111
1111
1110

[예제 출력 2]

-1

풀이

 문제의 큰 틀은 지나갈 수 있는 곳을 따라 최단 경로를 찾아가는 문제이다. 그렇기 때문에 그래프 탐색에서 너비 우선 탐색인 BFS를 적용하면 된다. 다만 이 문제 속 특정한 조건인 단 한 번에 한해서 벽을 부수고 이동을 할 수 있다는 조건 때문에, 문제 풀이가 좀 복잡해졌다.

 벽을 부순다는 조건으로 인해 복잡해지는 것이 바로 방문 여부에 대한 것이다. 기존에는 단순 BFS를 적용하면서 방문 여부 체크를 할 경우 당연히 가장 최단 경로가 해당 위치를 방문했다고 체크를 하기 때문에 늦어지는 경로는 자연스럽게 걸러진다. 하지만 이 상황에서는 당연히 해당 경로로 가기 위해 벽을 부수고 이동한 경로가 먼저 해당 지점을 방문하게 되는데, 만약 벽을 안 부수고 해당 지점에 도착한 경우가 나중에 다른 위치에서 벽을 부수고 이동함으로써 최단 경로가 될 수 있기 때문이다. 그렇다고 방문 여부 배열을 만들지 않고 BFS를 수행하면, 무한루프에 빠지게 된다.

 이를 해결하고자 적용한 것이 3차원 방문 배열로, 위치 정보 구조체에 벽을 부순 적이 있는 지에 대한 변수를 추가하고, 이를 바탕으로 특정 지점의 방문 여부를 2가지로 나눈 것이다. 이로써 특정 위치의 방문 여부에 대해 벽을 부순 경우끼리, 벽을 부수지 않은 경우끼리만 경로를 걸러낼 수 있어 최단 경로를 얻어낼 수 있다.

 

#include <iostream>
#include <vector>
#include <queue>

using namespace std;

int visited[1000][1000][2] = { 0, };

struct infor {//위치 정보 구조체
	int r, c;
	int cost;
	int used;
};

int BFS(vector<vector<int>> &map, int N, int M) {
	queue<infor> q;
	infor start = { 0,0,1,0 };
	q.push(start);

	while (!q.empty()) {
		infor here = q.front();
		q.pop();

		if (0 > here.r || here.r >= N || 0 > here.c || here.c >= M)//인덱스 밖
			continue;

		if (here.r == N - 1 && here.c == M - 1) //목표지점
			return here.cost;

		if(visited[here.r][here.c][here.used] == 1)//방문한 경우
			continue;

		if (map[here.r][here.c] == 1) {//현 지점이 벽
			if (here.used == 1) //이미 벽을 부셨다면
				continue;
			else //벽을 부신적이 없다면
				here.used = 1;
		}

		visited[here.r][here.c][here.used] = 1;

		++here.cost;

		++here.r;//하
		q.push(here);
		--here.r; ++here.c;//우
		q.push(here);
		here.c -= 2;//좌
		q.push(here);
		++here.c; --here.r;//상
		q.push(here);
	}

	return -1;
}

int main() {
	int N, M;
	int answer;
	vector<vector<int>> map;
	cin >> N >> M;

	for (int i = 0; i < N; i++) {//초기화
		vector<int> tmp(M, 0);
		string s;
		cin >> s;
		
		for (int j = 0; j < M; j++)
			tmp[j] = s[j] - '0';

		map.push_back(tmp);
	}

	answer = BFS(map, N, M);

	cout << answer;
	
	return 0;
}

결과

'Algorithm > 코드 풀이' 카테고리의 다른 글

2589번: 보물섬  (0) 2021.05.12
1707번: 이분 그래프  (0) 2021.05.08
16236번: 아기 상어  (0) 2021.04.30
1937번: 알파벳  (0) 2021.04.30
1238번: 파티  (0) 2021.04.26