백준

백준 : 2206번 벽 부수고 이동하기 BFS(c++)

TIN9 2023. 7. 16.
반응형

백준 링크

https://www.acmicpc.net/problem/2206

 

2206번: 벽 부수고 이동하기

N×M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 당신은 (1, 1)에서 (N, M)의 위치까지 이동하려 하는데, 이때 최단 경로

www.acmicpc.net

시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율
2 초 192 MB 118570 30406 18946 23.017%

문제

N×M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 당신은 (1, 1)에서 (N, M)의 위치까지 이동하려 하는데, 이때 최단 경로로 이동하려 한다. 최단경로는 맵에서 가장 적은 개수의 칸을 지나는 경로를 말하는데, 이때 시작하는 칸과 끝나는 칸도 포함해서 센다.

만약에 이동하는 도중에 한 개의 벽을 부수고 이동하는 것이 좀 더 경로가 짧아진다면, 벽을 한 개 까지 부수고 이동하여도 된다.

한 칸에서 이동할 수 있는 칸은 상하좌우로 인접한 칸이다.

맵이 주어졌을 때, 최단 경로를 구해 내는 프로그램을 작성하시오.

입력

첫째 줄에 N(1 ≤ N ≤ 1,000), M(1 ≤ M ≤ 1,000)이 주어진다. 다음 N개의 줄에 M개의 숫자로 맵이 주어진다. (1, 1)과 (N, M)은 항상 0이라고 가정하자.

출력

첫째 줄에 최단 거리를 출력한다. 불가능할 때는 -1을 출력한다.

예제 입력 1

6 4
0100
1110
1000
0000
0111
0000

예제 출력 1

15

예제 입력 2

4 4
0111
1111
1111
1110

예제 출력 2

-1

코드 풀이 BFS

기본 경로탐색이랑 다른 부분은 벽을 한 번 부실 수 있다는 것이다.

그래서 벽을 부셨는지 판단하기 위해 위치와 파괴했는지의 bool변수를 저장할 Node라는 구조체를 만들어 사용하였고 Dist배열도 3차원 배열로 벽을 깼을 때와 안 깼을 때의 값 두 개를 저장할 수 있도록 구현

1. 입력값이 띄어쓰기로 구분이 안되어 있기 때문에 string으로 받아서 분할하여 Board에 입력

2. BFS 탐색 - 기존 BFS와 동일하게 4방향을 탐색하는데 이때 2가지 방식으로 구분

2.a 다음 칸이 벽이고 아직 벽을 깨지 않은 상태라면 다음 위치의 벽을 부신 뒤 큐에 깼다고 저장, Dist[NextY][NextX][1]에 Dist[Cur.Y][Cur.X][0] + 1의 값을 대입

2.b 벽이 아니고 방문하지 않은 노드라면 다음 노드의 위치와 현재 노드에서의 IsCrash(벽을 이미 깬 상탠지 확인하는 변수)를 저장 후 Dist[NextY][NextX][IsCrash] = Dist[Cur.Y][Cur.X][IsCrash] + 1;

3. 최종 위치에 도달했을 경우 현재 노드의 정보를 바탕으로 한 거리값 출력

코드

#include <iostream>
#include <string>
#include <queue>
#include <algorithm>

using namespace std;

int Board[1001][1001];
int Dist[1001][1001][2];

int dx[4] = { 1, 0, -1, 0 };
int dy[4] = { 0, 1, 0, -1 };

struct Node
{
    int Y;
    int X;
    bool IsCrash;   // 벽을 파괴했는지 판단
};

int BFS(int N, int M)
{
    queue<Node> qNode;
    qNode.push({ 0, 0, false });
    
    Dist[0][0][0] = 1;

    while (!qNode.empty())
    {
        Node Cur = qNode.front();
        qNode.pop();

        if (Cur.X == M - 1 && Cur.Y == N - 1)
        {
            return Dist[Cur.Y][Cur.X][Cur.IsCrash];
        }

        for (int Dir = 0; Dir < 4; ++Dir)
        {
            int NextX = Cur.X + dx[Dir];
            int NextY = Cur.Y + dy[Dir];
            bool IsCrash = Cur.IsCrash;

            if (NextX < 0 || NextY < 0 || NextX >= M || NextY >= N)
                continue;

            // 다음 칸이 벽이고 아직 벽을 깨지 않은 상태라면
            if (Board[NextY][NextX] == 1 && IsCrash == false)
            {
                qNode.push({ NextY, NextX, true });
                Dist[NextY][NextX][1] = Dist[Cur.Y][Cur.X][IsCrash] + 1;
            }

            // 벽이 아니고 방문하지 않은 노드라면
            // Dist가 0이면 방문하지 않은 노드라는 뜻
            else if (Board[NextY][NextX] == 0 && Dist[NextY][NextX][IsCrash] == 0)
            {
                qNode.push({ NextY, NextX, IsCrash });
                Dist[NextY][NextX][IsCrash] = Dist[Cur.Y][Cur.X][IsCrash] + 1;
            }
        }
    }

    return -1;
}

int main()
{
    int N, M = 0;

    cin >> N>> M ;

    for (int i = 0; i < N; ++i)
    {
        // 입력받는 숫자가 연결되어 입력되고 있어서 스트링으로 받고 분할
        string Numb;
        cin >> Numb;
        for (int j = 0; j < M; ++j)
        {
            Board[i][j] = Numb[j] - 48;
        }
    }

    cout << BFS(N, M);

    return 0;
}
반응형

댓글