https://www.acmicpc.net/problem/2206
기본적으로 (1, 1)부터 (N, M)까지 BFS를 이용해서 최단 거리를 구하면 되는데, 벽을 최대 1번 부술 수 있다는 조건을 처리할 방법을 떠올리는 것이 어려웠다.
BFS의 시작부터 끝까지 벽을 부순 횟수를 가지고 있어야 하는데, 하나의 변수로 이를 관리하기에는 커버하지 못하는 경우가 발생하므로 적절하지 않다. (만약 broken이라는 변수로 벽을 부순 횟수를 0 또는 1로 저장한다면, 어느 시점에서 broken을 1로 만들지 정하기 어렵다.)
따라서 모든 경우를 커버할 수 있도록 dist 배열을 3차원으로 만들어서
dist[x][y][broken]에 현재까지 벽을 부순 횟수가 broken일 때 (1, 1)부터 (x, y)까지의 최단 거리를 저장한다.
현재 칸에서 다음 칸으로 이동하고자 할 때, 가능한 경우는 아래와 같다.
#include <bits/stdc++.h>
using namespace std;
struct Room {
int x, y, broken;
};
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int N, M;
cin >> N >> M;
vector<string> grid(N);
for(int i=0; i<N; i++) {
cin >> grid[i];
}
vector<vector<vector<int>>> dist(N, vector<vector<int>>(M, vector<int>(2, -1)));
queue<Room> q;
q.push({0, 0, 0});
dist[0][0][0] = 1;
const int dx[] = {-1, 1, 0, 0};
const int dy[] = {0, 0, -1, 1};
while(!q.empty()) {
auto [x, y, broken] = q.front();
q.pop();
int d = dist[x][y][broken];
if(x == N-1 && y == M-1) {
cout << d;
return 0;
}
for(int i=0; i<4; i++) {
int nx = x + dx[i];
int ny = y + dy[i];
if(nx < 0 || nx >= N || ny < 0 || ny >= M) continue;
if(grid[nx][ny] == '0') {
if(dist[nx][ny][broken] == -1) {
dist[nx][ny][broken] = d + 1;
q.push({nx, ny, broken});
}
} else {
if(broken == 0 && dist[nx][ny][1] == -1) {
dist[nx][ny][1] = d + 1;
q.push({nx, ny, 1});
}
}
}
}
cout << -1;
return 0;
}
BFS의 노드는 N×M개, 간선은 최대 4×N×M개이므로 O(NM)이다.
1️⃣ 정수형 벡터 dist를 사용하는 대신, bool형 배열 visited로 방문 여부, 정수형 변수로 최단 거리를 분리하여 관리한다.
2️⃣ visited[x][y][broken] 대신, visited[broken][y][x]으로 broken 차원을 앞에 둔다.