본문 바로가기
백준

[2206번] 벽 부수고 이동하기

by 2744m 2019. 5. 27.

간단해 보이는 탐색 문제지만 상당히 고생을 한 문제다.

문제 설명을 보면

벽을 1회 부술 기회를 준다.

처음 구현했을 때, 시간 복잡도를 계산해보지 않고 벽들을 하나하나 백트래킹을 이용해 부수고 

BFS로 탐색을 진행했다.

당연히 시간초과

그 다음으로 구현을 했을 때 방문 체크시 이전 방문지에서 벽을 부수고 도달했는지 부수지 않고 도달했는지를 구조체 배열을 이용해 함께 체크를 해주었다.

또 여기서 문제 발견

이미 방문한 곳을 안가도록 해두었더니 벽을 뚫고 먼저 진행해버린 곳은 진행 불가

예외케이스 발생으로 틀렸다.

다시 분기를 생각해 본 결과

1. 현재 위치가 벽을 부수지 않고 도달한 경우

    1.1 다음 탐색을 할 곳이 방문하지 않은 곳이거나 벽을 부수고 도착한 곳일 경우

         1.1.1 다음 탐색할 곳이 길  --> 탐색 

         1.1.2 다음 탐색할 곳이 벽  --> 탐색(벽을 부순것을 체크)

    1.2 다음 탐색할 곳이 방문하지 않은 곳 --> 탐색 o

2. 현재 위치가 벽을 부수고 도달한 경우

   2.1 다음 탐색할 곳이 방문하지 않은 곳

         2.1.1 다음 탐색할 곳이 길 --> 탐색o

         2.1.2. 다음 탐색할 곳이 벽 --> 탐색x

 

이렇게 분기를 나눠 풀었다.

큐도 연결리스트를 이용해 간단히 구현하여 사용하였다.

/*2206번 벽 부수고 이동하기*/
#include <iostream>
using namespace std;
int N, M;
char Map[1001][1001];
int drc[] = { -1,0,1,0,0,1,0,-1 };//상 우 하 좌 탐색

typedef struct pnt { int r, c;}pnt;
typedef struct V { int visit; bool dstchk; }V;
V visit[1000][1000];
pnt temp;

typedef struct node {
	pnt value;
	struct node* next;
}node;

typedef struct queue {
	node *front, *rear;
	int len;
	queue() {
		front = rear = NULL;
		len = 0;
	}
}queue;

void Q_push(queue *Q, pnt value) {
	node *new_node = new node;
	new_node->value = value;
	new_node->next = NULL;
	if (Q->front == NULL) { // 큐가 비었을 때
		Q->front = new_node;
		Q->rear = new_node;
	}
	else {
		Q->rear->next = new_node;
		Q->rear = new_node;
	}
	Q->len++;
}

void Q_pop(queue *Q) {
	node *temp = Q->front;
	Q->front = Q->front->next;
	delete temp;
	Q->len--;
}

void BFS(int r, int c) {
	queue *Q = new queue;
	temp = { r,c };
	Q_push(Q, temp);
	visit[r][c] = { 1,false };
	while (Q->len > 0) {
		int cur_r = Q->front->value.r;
		int cur_c = Q->front->value.c;
		Q_pop(Q);
		for (int d = 0; d < 4; d++) {
			int nr = cur_r + drc[d];
			int nc = cur_c + drc[d + 4];
			if (!(nr < 0 || nc < 0 || nr >= N || nc >= M)) {
				if (visit[cur_r][cur_c].dstchk == true) {//현 위치까지 벽을 부수고 도착했다.
					if (visit[nr][nc].visit == 0) {//다음 갈 곳이 방문하지 않은 곳
						if (Map[nr][nc] == '0') {//다음 갈 곳이 길이다.
							visit[nr][nc].visit = visit[cur_r][cur_c].visit + 1;
							visit[nr][nc].dstchk = visit[cur_r][cur_c].dstchk;
							temp = { nr,nc };
							Q_push(Q, temp);
						}
						//여기선 이미 벽을 부수고 왔기 때문에 다음 갈 곳이 벽이면 갈 수 없다.
						else continue;
					}
				}
				else {//현 위치까지 벽을 부수지 않고 도착했다.
					if (!(visit[nr][nc].visit!=0 && visit[nr][nc].dstchk==false)) {
						if (Map[nr][nc] == '0') {//다음 갈 곳이 길이다.
							visit[nr][nc].visit = visit[cur_r][cur_c].visit + 1;
							visit[nr][nc].dstchk = visit[cur_r][cur_c].dstchk;
							temp = { nr,nc };
							Q_push(Q, temp);
						}
						else {//다음 갈 곳이 벽이다.
							visit[nr][nc].visit = visit[cur_r][cur_c].visit + 1;
							visit[nr][nc].dstchk = true;//벽 부수고 방문
							temp = { nr,nc };
							Q_push(Q, temp);
						}
					}
				}
				if (nr == N - 1 && nc == M - 1) return;
			}
		}
	}
}

int main() {
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cin >> N >> M;
	for (int r = 0; r < N; r++) cin >> Map[r];
	BFS(0, 0);
	if (visit[N - 1][M - 1].visit == 0) cout << -1;
	else cout << visit[N - 1][M - 1].visit;
}

 

도움이 되었던 예외 케이스

8 8
01000100
01010100
01010100
01010100
01010100
01010100
01010100
00010100

답 : 29

'백준' 카테고리의 다른 글

[11758번] ccw  (0) 2019.08.13
[2133번] 타일 채우기  (0) 2019.06.11
[2609번] 최대 공약수와 최대 공배수  (0) 2019.04.17
[1929번] 소수 구하기  (0) 2019.04.17
[1978번] 소수 찾기  (0) 2019.04.17

댓글