BOJ16933

벽부수고이동하기 3

문제출처

문제 해석

  • (1,1)에서 (N,M)으로 이동하기 위한 최단 경로를 구하는 문제입니다.
  • 이동할 때에는 이동하지 않고 같은 칸에 머물러 있는 것도 가능합니다. (이동횟수는 증가합니다)
  • 낮과 밤이 존재하는데 낮에는 벽을 K개 까지 부술 수 있습니다.(한번에 한개씩만 부숩니다)
  • 처음 이동할 때는 낮이고, 낮과 밤이 번갈아 발생합니다.
  • N,M <= 1000, 1<= K <= 10

설계(20)

  • 최소 이동거리를 구하는 문제이므로 bfs를 사용합니다.
  • 이동 상태를 표현하기 위해서는 {x,y,벽을 부순 개수, 낮 or 밤} 이 필요한데 처음에 이동할 때 낮이라고 문제에서 언급한 바가 있기 때문에 저는 초기 상태를 밤이라고 설정하였습니다.
  • 탐색을 진행할 때, 먼저 낮과 밤의 경우를 나누고 낮인 경우 벽을 부순 개수가 K개 미만이고(K개를 부쉈으면 더이상 부술 수 없습니다), 방문한 적이 없으면 진행하도록 하였습니다.
  • 밤의 경우에는 이동할 칸이 벽이면 제자리에 머물도록 하였습니다.
  • 현재 칸이 x == N-1 && y == M-1인경우 이동횟수 + 1 을 리턴하는 식으로 구현하였습니다.

구현(40)

코드
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define MAXN 1000
int N, M, K;
bool visited[MAXN][MAXN][11][2];
int map[MAXN][MAXN];
int dx[] = { 0,0,1,-1 };
int dy[] = { 1,-1,0,0 };
struct node {
	int x;
	int y;
	int cnt;
	int state;
};
int bfs() {
	queue<node> q;
	q.push({ 0,0,0,1 });
	visited[0][0][0][1] = true;
	int ans = 0;
	while (!q.empty()) {
		int q_size = q.size();

		while (q_size--) {
			int x = q.front().x;
			int y = q.front().y;
			int cnt = q.front().cnt;
			int state = q.front().state;
			if (x == N - 1 && y == M - 1) return ans + 1;
			q.pop();
			for (int k = 0; k < 4; k++) {
				int nx = x + dx[k];
				int ny = y + dy[k];
				if (nx < 0 || ny < 0 || nx >= N || ny >= M) continue;
				if (state == 1) {
					if (map[nx][ny] == 0) {
						if (visited[nx][ny][cnt][1-state]) continue;
						visited[nx][ny][cnt][1-state] = true;
						q.push({ nx,ny,cnt,1 - state });
					}
					else {
						if (cnt < K && !visited[nx][ny][cnt+1][1-state]) {
							visited[nx][ny][cnt + 1][1-state] = true;
							q.push({ nx,ny,cnt + 1,1 - state });
						}
					}
				}
				else {
					if (map[nx][ny] == 0) {
						if (visited[nx][ny][cnt][1-state]) continue;
						visited[nx][ny][cnt][1-state] = true;
						q.push({ nx,ny,cnt,1 - state });
					}
					else {
						if (visited[x][y][cnt][1 - state]) continue;
						visited[x][y][cnt][1 - state] = true;
						q.push({ x,y,cnt,1 - state });
					}
				}
				
			}
		}
		ans++;
	}
	return -1;
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	scanf("%d%d%d", &N, &M, &K);
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < M; j++) {
			scanf("%1d", &map[i][j]);
		}
	}
	
	cout << bfs() << "\n";
	
	
	return 0;
}
디버깅
  • 방문체크를 할 때, 각 낮과 밤 그리고 벽인지 빈 공간인지에 따라 다르게 체크를 하는 부분이 헷갈렸습니다.
제출결과
  • 540ms

마무리

  • 여러 조건이 부가된 까다로운 bfs문제였습니다. 조건을 부여하는데에 실수를 많이 일으키기 다분하므로 연습하기에 아주 좋은 문제인 것 같습니다.