프로그래밍/알고리즘

BOJ 2151 · 거울 설치

반응형


알고리즘 분류 : BFS  


문에서 다른 문까지 빛으로 연결하기 위해 필요한 거울의 최소 개수를 구하는 문제다. 빛은 거울에서만 꺾이고, 거울은 45도 각도로 설치할 수 있다. 거울을 적절히 설치해서 문에서 문으로 빛을 연결해야 한다.


  • 문('#')은 항상 2개만 주어지므로, 하나의 문을 시작점으로, 다른 하나의 문은 도착점으로 둔다.
  • BFS 탐색을 하면서 다음 이동 좌표가 갈 수 있는 곳인지 확인한다.
  • 방문 체크와 거울 설치 횟수를 저장할 배열 dist를 3차원으로 선언한다.
  • dist의 인덱스는 [X 좌표] [Y좌표] [이동 방향] 이다.
  • 만약 다음 이동 좌표가 맵의 밖이거나 벽('*')이라면, 이동할 수 없다.
  • 다음 이동 좌표가 빈 공간('.')이라면, 계속 이동한다.
  • while 반복문을 통해 다음 이동이 가능한 위치라면, 같은 방향으로 계속 이동한다.
  • 이동하다가 거울을 놓을 수 있는 장소('!')에 도착하면, 다음 이동 좌표로 3가지를 큐에 넣는다.
  • 첫 번째, 이전 이동 방향과 같은 방향의 좌표를 넣는다. (거울 설치를 하지 않는다.)
  • 두 번째, 방향을 +90도로 꺾는 좌표를 넣는다. (거울을 설치한다.)
  • 세 번째, 방향을 -90도로 꺾는 좌표를 넣는다. (거울을 설치한다.)
  • 위 과정을 도착지점에 도착할 때까지 반복한다.

  • 거울을 꺾는 방향 이동은 아래와 같이 나타낼 수 있다.
  • 우선 방향은 [↑0  ↓1  ←2  →3 ] 으로 둔다.
  • 위쪽(0) 방향은 왼쪽(2)이나 오른쪽(3)으로 꺾인다. [ 0 → 2 , 0 → 3 ]
  • 아래쪽(1) 방향은 왼쪽(2)이나 오른쪽(3)으로 꺾인다. [ 1 → 2 , 1 → 3 ]
  • 왼쪽(2) 방향은 위쪽(0)이나 아래쪽(1)으로 꺾인다. [ 2 → 0 , 2 → 1 ]
  • 오른쪽(3) 방향은 위쪽(0)이나 아래쪽(1)으로 꺾인다. [ 3 → 0 , 3 → 1 ]




C++ 소스코드


#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;

struct mirror {
    int x, y, z;
};

int n, ex, ey;
char a[50][50];
int dist[50][50][4];
queue<mirror> q;
const int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1};

bool move(int x, int y, int z) {
    if (x < 0 || x >= n || y < 0 || y >= n) return false;
    if (dist[x][y][z] == '*') return false;
    return true;
}

void bfs() {
    while (!q.empty()) {
        int x = q.front().x, y = q.front().y, z = q.front().z; q.pop();
        if (x == ex && y == ey) {
            printf("%d\n", dist[x][y][z]);
            return;
        }
        int nx = x+dx[z], ny = y+dy[z];
        while (move(nx, ny, z) && a[nx][ny] == '.') {
            nx += dx[z], ny += dy[z];
        }
        if (move(nx, ny, z) && a[nx][ny] == '!') {
            dist[nx][ny][z] = dist[x][y][z];
            q.push({nx, ny, z});
            int k = z < 2 ? 2 : 0;
            for (int i=k; i<k+2; i++) {
                if (dist[nx][ny][i] == -1) {
                    dist[nx][ny][i] = dist[x][y][z]+1;
                    q.push({nx, ny, i});
                }
            }
        }
    }
}

int main() {
    scanf("%d", &n);
    memset(dist, -1, sizeof(dist));
    for (int i=0; i<n; i++) {
        for (int j=0; j<n; j++) {
            scanf(" %1c", &a[i][j]);
            if (a[i][j] == '#') {
                if (q.empty()) {
                    for (int k=0; k<4; k++) {
                        q.push({i, j, k});
                        dist[i][j][k] = 0;
                    }
                } else {
                    ex = i, ey = j;
                }
                a[i][j] = '!';
            }
        }
    }
    bfs();
    return 0;
}




Python 3 소스코드


from collections import deque

def move(x, y, z):
    if x < 0 or x >= n or y < 0 or y >= n:
        return False
    if dist[x][y][z] == '*':
        return False
    return True

def bfs():
    while q:
        x, y, z = q.popleft()
        if x == ex and y == ey:
            print(dist[x][y][z])
            return
        nx, ny = x+dx[z], y+dy[z]
        while move(nx, ny, z) and a[nx][ny] == '.':
            nx, ny = nx+dx[z], ny+dy[z]
        if move(nx, ny, z) and a[nx][ny] == '!':
            dist[nx][ny][z] = dist[x][y][z]
            q.append((nx, ny, z))
            k = 2 if z < 2 else 0
            for i in range(k, k+2):
                if dist[nx][ny][i] == -1:
                    dist[nx][ny][i] = dist[x][y][z]+1
                    q.append((nx, ny, i))

ex, ey = 0, 0
dx, dy = (-1, 1, 0, 0), (0, 0, -1, 1)
n = int(input())
a = [list(input().strip()) for _ in range(n)]
dist = [[[-1]*4 for _ in range(n)] for _ in range(n)]
q = deque()
for i in range(n):
    for j in range(n):
        if a[i][j] == '#':
            if not q:
                for k in range(4):
                    q.append((i, j, k))
                    dist[i][j][k] = 0
            else:
                ex, ey = i, j
            a[i][j] = '!'
bfs()




참고



반응형