본문 바로가기

짜잘한 기록

백준 9328 열쇠

비트마스킹 문제를 푸려고 보는 중, 내가 분명히 풀었던건데 비트마스킹을 쓴 기억이 없어 들고와봤다. 내가 풀었을때는, BFS 탐색을 조금 예외상황을 넣으며 풀었다. 

 

처음 접근할때는, 맵을 곧이곧대로 입력받고 시작 점을 하나 잡아서 구현했었다. 이 경우 벽이 아닌곳을 통해 다른곳으로 이동하는 경우 처리가 너무 빡셌다. 그래서 그냥 맵 주변을 전부 접근가능한 . 로 표시하는 방법을 선택했다. 이 경우, 자연스럽게 BFS탐색으로 해당 케이스가 커버된다.

맵 입력을 받은 뒤, 열쇠 리스트를 입력으로 받아 해당 열쇠로 열 수 있는 문을 전부 갈 수 있는 '.'로 대체해준다. 열쇠 입수 순간에 맵에 표시를 한다는 생각은 차마 못했는데, 다른 솔루션에서 봤다. 입수 키 리스트를 들고, 문을 볼때마다 키 리스트와 대조해 진행여부를 판단하는 것은 너무 복잡도가 올라가는 구현이었다.

이후 BFS로 탐색을 한다. 탐색을 하며 문서를 만나면 입수 문서 수를 늘리고, 그 문서가 있던 자리를 '.'로 변경하며 계속 탐색을 한다. 만약, 열쇠를 입수하게 되면, 그 열쇠로 잠겨있던 문을 열고('.'로 표시) visited[][]배열을 초기화한다. 이 visited[][] 배열의 초기화 아이디어를 떠올리는게 어려웠다. 문으로 잠겨있던 곳을 다시 가려면 visited[][]로 이미 표시되어 있는 곳을 가야하므로 이 과정이 꼭 들어가야 한다...

 

그 과정을 진행하면! 답을 구할 수 있다...


#include <iostream>
#include <queue>
#include <string.h>

int dx[] = {1, -1, 0, 0};
int dy[] = {0, 0, 1, -1};
struct cur{
    int x;
    int y;
    bool key[26];
    int paperCnt;
    int keyCnt;
    cur (int y, int x, int cnt) : y(y), x(x), paperCnt(cnt) {}
    void setKey(bool keylist[])
    {
        for (int i = 0; i < 26; i ++)
        {
            this->key[i] = keylist[i];
            if (keylist[i])
                keyCnt++;
        }
    }
};

int main()
{
    int T;
    std::cin >> T;

    for (int Tcase = 0; Tcase < T; Tcase++)
    {
        int N, M;
        std::cin >> N >> M;
        std::string map[N+2]; //지도
        int visited[N+2][M+2]; //방문여부 체크
        bool key[26];

        memset(visited, 0, sizeof(visited));
        memset(key, 0, sizeof(key));
        for (int i = 0 ; i < N + 2; i ++)
            map[i].clear();
        for (int i = 0; i < M + 2; i++)
            map[0] += '.';
        for (int i = 1; i <= N; i++)
        {
            std::string inp;
            std::cin >> inp;
            map[i] += '.';
            map[i] += inp;
            map[i] += '.';
        }
        for (int i = 0; i < M + 2; i++)
            map[N+1] += '.';
        std::string keyList;
        std::cin >> keyList;
        std::queue<cur> Q;
        if (keyList[0] != '0') //소유하고 있는 열쇠들 문 전부 열어두기('.' 으로 바꾸기.)
        {
            for (int i = 0; i < keyList.size(); i++)
            {
                key[keyList[i] - 'a'] = true;
                for (int y = 1; y <= N; y++)
                {
                    for (int x = 1; x <= M; x++)
                    {
                        if (map[y][x] == (char)std::toupper(keyList[i]))
                            map[y][x] = '.';
                    }
                }
            }
        }
        // 맵 가장자리가 아니라 시작점 일일히 찾았던 코드 로직 흔적.
        // for (int i = 0; i < M; i++)
        // {
        //     if (map[0][i] != '*')
        //     {
        //         cur now(0, i, 1);
        //         now.setKey(initKeylist);
        //         if ('a' <= map[0][i] && map[0][i] <= 'z')
        //             now.key[map[0][i] - 'a'] = true;
        //         Q.push(now);
        //     }
        //     if (map[N-1][i] != '*')
        //     {
        //         cur now(N-1, i, 1);
        //         now.setKey(initKeylist);
        //         if ('a' <= map[N-1][i] && map[N-1][i] <= 'z')
        //             now.key[map[N-1][i] - 'a'] = true;
        //         Q.push(now);
        //     }
        // }
        // for (int i = 0; i < N; i++)
        // {
        //     if (map[i][0] != '*')
        //     {
        //         cur now(i, 0, 1);
        //         now.setKey(initKeylist);
        //         if ('a' <= map[i][0] && map[i][0] <= 'z')
        //             now.key[map[i][0] - 'a'] = true;
        //         Q.push(now);
        //     }
        //     if (map[i][M-1] != '*')
        //     {
        //         cur now(i, M-1, 1);
        //         now.setKey(initKeylist);
        //         if ('a' <= map[i][M-1] && map[i][M-1] <= 'z')
        //             now.key[map[i][M-1] - 'a'] = true;
        //         Q.push(now);
        //     }
        // }
        //std::cout << " ";

        /*
        BFS 로직 짤 때, 현재 위치 받아서 visited 체크및 처리 후 탐색 vs 현재 위치 받아서 다음것 탐색 + visited 체크 차이.
        */
        Q.push(cur(0,0,0));
        int docs = 0;
        while(!Q.empty())
        {
            cur now = Q.front(); Q.pop();
            int nx = now.x ;
            int ny = now.y ;

            if (nx < 0 || nx > M + 1 | ny <0 || ny > N+1 || map[ny][nx] == '*' || ('A' <= map[ny][nx] && map[ny][nx] <= 
            'Z') || visited[ny][nx])
                continue;
            visited[ny][nx] = true;

            if (map[ny][nx] == '$')
            {
                now.paperCnt++;
                docs++;
                map[ny][nx] = '.';
            }            

            if ('a' <= map[ny][nx] && map[ny][nx] <= 'z')
            {
                char door = (char)std::toupper(map[ny][nx]);
                map[ny][nx] = '.';

                if (key[door - 'A'] == false)
                {
                    key[door - 'A'] = true;
                    for (int y = 1; y <= N; y++)
                    {
                        for (int x = 1; x <= M; x++)
                        {
                            if (map[y][x] == door)
                                map[y][x] = '.'; // 해당 키로 잠긴 문 열어두기('.'로 바꾸기)
                        }
                    }
                }
                //잠긴 문이 열렸으므로, 모든 경로 다시 확인 필요.
                memset(visited, false, sizeof(visited));
                while(!Q.empty())
                    Q.pop();
                Q.push(cur(ny, nx, now.paperCnt));
            }
            //동서남북 탐색

            for(int i = 0; i < 4; i++)
            {
                int nextX = nx + dx[i];
                int nextY = ny + dy[i];
                Q.push(cur(nextY, nextX, now.paperCnt));
            }
                // 탐색 조건 추가. 키 다 찾기 전에는 visit 표시 안하고, 탐색 다 하면 visit 표시하면서 탐색 X -> 저번 구현때 시도했지만 현 구현에서는 필요없음
        }
        std::cout <<  docs << '\n';
    }
}

Bitmask 구현도 알아봤다. Bitmask 쓴다고 좀 기대했었는데, 그냥 현재 가지고 있는 키 상황을 int변수로 저장하는게 다였다... 그래도 처음 내가 접근했던 방식인 문 만날때 키 확인을 bitmask 방식으로 구현하면 조금 간략해지는것 같다. Bitmask 솔루션은 여기서 봤다.

 

오늘도 평온한 하루가 되길. 슨민.

'짜잘한 기록' 카테고리의 다른 글

백준 14939 불 끄기  (0) 2021.10.27
백준 14391 종이 조각  (0) 2021.10.26
백준 1562 계단 수  (0) 2021.10.21
백준 11578 팀원 모집  (0) 2021.10.20
백준 13701 중복 제거  (0) 2021.10.19