문제
N×M크기의 배열로 표현되는 미로가 있다.
| 1 | 0 | 1 | 1 | 1 | 1 |
| 1 | 0 | 1 | 0 | 1 | 0 |
| 1 | 0 | 1 | 0 | 1 | 1 |
| 1 | 1 | 1 | 0 | 1 | 1 |
미로에서 1은 이동할 수 있는 칸을 나타내고, 0은 이동할 수 없는 칸을 나타낸다. 이러한 미로가 주어졌을 때, (1, 1)에서 출발하여 (N, M)의 위치로 이동할 때 지나야 하는 최소의 칸 수를 구하는 프로그램을 작성하시오. 한 칸에서 다른 칸으로 이동할 때, 서로 인접한 칸으로만 이동할 수 있다.
위의 예에서는 15칸을 지나야 (N, M)의 위치로 이동할 수 있다. 칸을 셀 때에는 시작 위치와 도착 위치도 포함한다.
입력
첫째 줄에 두 정수 N, M(2 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 M개의 정수로 미로가 주어진다. 각각의 수들은 붙어서 입력으로 주어진다.
출력
첫째 줄에 지나야 하는 최소의 칸 수를 출력한다. 항상 도착위치로 이동할 수 있는 경우만 입력으로 주어진다.
문제 링크
https://www.acmicpc.net/problem/2178
풀이
그래프 이론에서의 너비 우선 탐색, BFS를 사용하면 해결되는 문제다.
1. 가장 먼저 미로와 거리를 나타내는 배열을 만들어 준다.▼

2. 그리고 미로의 (0, 0)부터 탐색을 시작해준다.
탐색을 할 때는 상, 하, 좌, 우 를 검사하는데 맵 밖이거나 갈 수 없는 곳이라면 스킵한다.
하지만 만약 갈 수 있는 곳이라면 다음에 갈 곳이므로, 다음에 갈 곳을 모아놓은 큐에 넣어준다.
(큐에 넣은 값들은 y, x 순서이다.)
현재 좌표와 동일한 좌표의 `distance`값에 직전 좌표의 `distance + 1`을 넣어주는데, 현재는 직전 값이 없으므로 그냥 `1`을 넣어준다. ▼

3. 다음에는 큐에 가장 먼저 들어갔던 좌표를 pop 해주고, 그 좌표로 이동한다.
2번이랑 똑같은 과정을 반복해주면 된다.
이 때 바로 뒤의 값은 이미 방문했던 값이므로 큐에 넣어주지 않는다.
아래에 첨부된 코드랑은 약간 다르지만, 여기서 설명하는 걸로는 `distance`값이 `0`이냐, `0`이 아니냐로 방문 여부를 판단할 것이다.
(0, 0)은 `distance` 값이 `1`이므로 이미 방문한 곳이라 더 이상 방문해주지 않는다. ▼

4. 위 과정을 끝에 도달할 때 까지 방문해주면 된다.
문제에서 항상 도착위치로 이동할 수 있는 경우만 입력으로 준다고 했으므로, 중간에 막히는 경우는 생각하지 않아도 된다.
위의 과정대로 전부 진행해주면, 아래와 같은 결과가 나오고 오른쪽 끝 값을 출력해주면 된다.▼

이 문제에서 BFS를 사용하는 이유를 아주 간단하게 얘기하자면,
DFS의 경우엔 결과가 최단 거리라는 보장이 없지만, BFS는 결과가 최단 거리라는 보장이 있기 때문이다.
C++ 코드
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <utility>
using namespace std;
int N, M;
int dy[] = {1, -1, 0, 0};
int dx[] = {0, 0, 1, -1};
vector<vector<int>> map;
vector<vector<int>> visited;
string tmp;
void input() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
cin >> N >> M;
for (int i = 0; i < N; i++) {
map.push_back(vector<int>(M));
visited.push_back(vector<int>(M));
}
for (int i = 0; i < N; i++) {
cin >> tmp;
for (int j = 0; j < M; j++) {
map[i][j] = tmp[j] - 48;
}
}
}
void solution() {
queue<pair<int, int>> q;
q.push({0, 0});
visited[0][0] = 1;
while (q.size() != 0) {
pair<int, int> cur;
cur = q.front();
q.pop();
for (int i = 0; i < 4; i++) {
if (cur.first + dy[i] < 0 || cur.first + dy[i] >= N || cur.second + dx[i] < 0 || cur.second + dx[i] >= M) {
continue ;
}
if (visited[cur.first + dy[i]][cur.second + dx[i]] != 0) {
continue ;
}
if (map[cur.first + dy[i]][cur.second + dx[i]] == 0) {
continue ;
}
q.push({cur.first + dy[i], cur.second + dx[i]});
visited[cur.first + dy[i]][cur.second + dx[i]] = visited[cur.first][cur.second] + 1;
}
}
}
void solve() {
input();
solution();
cout << visited[N - 1][M - 1];
}
int main() {
solve();
}
Swift 코드
let firstLine = readLine()!.split(separator: " ").map({Int($0)!})
let n = firstLine[0]
let m = firstLine[1]
var maze = [[Int]]()
for _ in 0..<n {
maze.append(readLine()!.map({Int(String($0))!}))
}
var visited: [[Int]] = [[Int]](repeating: [Int](repeating: -1, count: m), count: n)
var queue: [[Int]] = [[0,0]]
let dx: [Int] = [0,0,-1,1]
let dy: [Int] = [-1,1,0,0]
var predecessor: [[[Int]]] = [[[Int]]](repeating: [[Int]](repeating: [-1, -1], count: m), count: n)
predecessor[0][0] = [0,0]
var distance: [[Int]] = [[Int]](repeating: [Int](repeating: 0, count: m), count: n)
distance[0][0] = 1
while queue.count != 0 {
let now = queue.remove(at: 0)
if visited[now[0]][now[1]] == -1 {
visited[now[0]][now[1]] = 1
for i in 0..<dx.count {
let nowdx = now[0] - dx[i]
let nowdy = now[1] - dy[i]
if nowdx < 0 || nowdx > n-1 || nowdy < 0 || nowdy > m-1 {
continue
} else {
if maze[nowdx][nowdy] == 1 && visited[nowdx][nowdy] == -1 {
predecessor[nowdx][nowdy] = now
distance[nowdx][nowdy] = distance[now[0]][now[1]] + 1
queue.append([nowdx, nowdy])
}
}
}
}
}
print(distance[n-1][m-1])
위의 설명을 보고 오면 ‘visited는 뭐지…’ 싶을 수도 있다.
`visited`는 방문 여부를 나타낸 2차원 배열이므로 `distance`로도 충분히 체크할 수 있는 부분이라 그냥 `distance`로 체크한다고 설명을 해놨다.
그래서 `visited`는 그저 `distance`에서 떨어져 나온 방문 체크용 배열이라고 생각하면 된다.
'Algorithm > PS' 카테고리의 다른 글
| 백준 1402번 아무래도 이 문제는 A번 난이도인 것 같다 - C++ (0) | 2025.02.02 |
|---|---|
| 백준 11722번 가장 긴 감소하는 부분수열 - C++ (0) | 2024.11.23 |
| 백준 2116번 주사위 쌓기 - C++ (0) | 2024.11.21 |
| 프로그래머스 전력망을 둘로 나누기 - C++ (0) | 2024.11.20 |
| 프로그래머스 소수 찾기 - C++ (0) | 2024.11.19 |