Game Develop

[Algorithm] Baekjoon 9328번 : 열쇠 본문

Algorithm/Baekjoon

[Algorithm] Baekjoon 9328번 : 열쇠

MaxLevel 2023. 4. 29. 04:08

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

 

9328번: 열쇠

상근이는 1층 빌딩에 침입해 매우 중요한 문서를 훔쳐오려고 한다. 상근이가 가지고 있는 평면도에는 문서의 위치가 모두 나타나 있다. 빌딩의 문은 모두 잠겨있기 때문에, 문을 열려면 열쇠가

www.acmicpc.net

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
struct Node
{
    int y;
    int x;
};
 
 
int t, h, w;
vector<string> arr;
int dir[4][2= { {-1,0}, {1,0},{0,-1}, {0,1} };
map<charbool> keys; // 키값 소문자
bool visited[101][101= { false };
 
vector<Node> candidateNodes; // 더 탐색가능한 노드들을 담아둔다.
map<char,vector<Node>> lockedDoorNodes; // 잠긴문을 만나면 일단 무조건 넣는다. 키값 대문자
vector<int> answer;
int answerCount = 0;
 
 
void BFS()
{
    queue<Node> q;
 
    for (int i = 0; i < candidateNodes.size(); ++i)
    {
        q.push(candidateNodes[i]);
        visited[candidateNodes[i].y][candidateNodes[i].x] = true;
    }
 
    candidateNodes.clear();
 
    while (!q.empty())
    {
        int curY = q.front().y;
        int curX = q.front().x;
        
        q.pop();
 
        for (int i = 0; i < 4++i)
        {
            int nextY = curY + dir[i][0];
            int nextX = curX + dir[i][1];
 
            if (nextY < 0 || nextY >= h) continue;
            if (nextX < 0 || nextX >= w) continue;
            if (arr[nextY][nextX] == '*'continue;
            if (visited[nextY][nextX]) continue;
 
            // 빈공간 or 문 or 열쇠 or 문서
 
            if (arr[nextY][nextX] == '.')
            {
                visited[nextY][nextX] = true;
                q.push({ nextY,nextX });
            }
            else if (arr[nextY][nextX] >= 'A' && arr[nextY][nextX] <= 'Z'// 문이면
            {
                if (keys[arr[nextY][nextX] + 32]) // 키 있으면
                {
                    visited[nextY][nextX] = true;
                    q.push({ nextY,nextX });
                }
                else // 키 없으면 후보로 등록.
                {
                    visited[nextY][nextX] = true;
                    lockedDoorNodes[arr[nextY][nextX]].push_back({ nextY,nextX });
                }
            }
            else if (arr[nextY][nextX] >= 'a' && arr[nextY][nextX] <= 'z'// 열쇠면
            {
                visited[nextY][nextX] = true;
                keys[arr[nextY][nextX]] = true;
                q.push({ nextY,nextX });
            }
            else if (arr[nextY][nextX] == '$')
            {
                ++answerCount;
                visited[nextY][nextX] = true;
                q.push({ nextY,nextX });
            }
        }
    }
 
    // 여기서 locked검사해서 candidate넣어줄만한거 넣어주기.
 
    for (auto iter = lockedDoorNodes.begin(); iter != lockedDoorNodes.end(); ++iter)
    {
        int vSize = iter->second.size();
 
        for (int i = vSize-1; i >= 0--i)
        {
            if (keys[iter->first + 32])
            {
                candidateNodes.push_back(iter->second[i]);
                iter->second.pop_back();
            }
        }
    }
 
}
 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
 
    cin >> t;
 
    for (int i = 0; i < t; ++i)
    {
        memset(visited, falsesizeof(visited));
        candidateNodes.clear();
        arr.clear();
        keys.clear();
        lockedDoorNodes.clear();
        answerCount = 0;
 
        cin >> h >> w;
 
        for (int row = 0; row < h; ++row)
        {
            string temp;
            cin >> temp;
            arr.push_back(temp);
        }
 
        string keyInfo;
        cin >> keyInfo;
 
        if (keyInfo[0!= '0')
        {
            for (int j = 0; j < keyInfo.size(); ++j)
            {
                keys[keyInfo[j]] = true;
            }
        }
 
        for (int i = 0; i < h; ++i)
        {
            for (int j = 0; j < w; ++j)
            {
                // 외곽 검사
 
                if (i == 0 || i == h - 1// 맨 위 맨 아래
                {
                    if (arr[i][j] == '.')
                    {
                        candidateNodes.push_back({ i,j });
                    }
                    else if (arr[i][j] >= 'A' && arr[i][j] <= 'Z'// 문인데
                    {
                        if (keys[arr[i][j] + 32]) // 열쇠있으면
                        {
                            candidateNodes.push_back({ i,j });
                        }
                        else // 없으면 검사는 해야하니까 lock에 넣어놓고
                        {
                            lockedDoorNodes[arr[i][j]].push_back({ i,j });
                        }
                    }
                    else if (arr[i][j] >= 'a' && arr[i][j] <= 'z'// 열쇠면
                    {
                        keys[arr[i][j]] = true;
                        candidateNodes.push_back({ i,j });
                    }
                    else if (arr[i][j] == '$')
                    {
                        ++answerCount;
                        candidateNodes.push_back({ i,j });
                    }
                }
                else
                {
                    if (j == 0 || j == w - 1)
                    {
                        if (arr[i][j] == '.')
                        {
                            candidateNodes.push_back({ i,j });
                        }
                        else if (arr[i][j] >= 'A' && arr[i][j] <= 'Z'// 문인데
                        {
                            if (keys[arr[i][j] + 32]) // 열쇠있으면
                            {
                                candidateNodes.push_back({ i,j });
                            }
                            else // 없으면 검사는 해야하니까 lock에 넣어놓고
                            {
                                lockedDoorNodes[arr[i][j]].push_back({ i,j });
                            }
                        }
                        else if (arr[i][j] >= 'a' && arr[i][j] <= 'z'// 열쇠면
                        {
                            keys[arr[i][j]] = true;
                            candidateNodes.push_back({ i,j });
                        }
                        else if (arr[i][j] == '$')
                        {
                            ++answerCount;
                            candidateNodes.push_back({ i,j });
                        }
                    }
                }
 
            }
        }
 
        while (!candidateNodes.empty())
        {
            BFS();
        }
 
        answer.push_back(answerCount);
    }
 
    for (int i = 0; i < answer.size(); ++i)
    {
        cout << answer[i] << endl;
    }
}
cs

 

조건이 많아서 그렇지, 사실 어렵지는 않은 문제.

특히 문제를 보자마자 예외케이스 몇개가 바로 생각나서 다행히 크게 고생은 안했다.

 

탐색전에 외각에서부터 시작해야하니, 진입가능한 노드들을 미리 구해서 candidateNodes.에 담아놓는다.

그리고 그걸 기반으로 BFS를 수행하며, 수행하는 도중 문을 만났을 때 열쇠가 있다면 그대로 탐색하면되고 문이 없다면 일단 lockedDoorNodes라는 곳에 보관한다. 

이후 메인BFS로직이 끝나고 나면, lockedDoorNodes를 순회하면서 잠긴문좌표노드에 대해 검사를 하는데, 해당 노드에대해 열쇠가 있다면 candidateNodes로 옮긴다.

즉 candidateNodes는 실제로 탐색시작가능한 노드들만 담는 곳이다.

 

이렇게 반복하다가 candidateNodes가 텅 비어버린다면, 더이상 탐색할 공간이 전혀 없다는 의미이니 해당 테스트케이스를 종료하면 된다.

 

헷갈리기 싫어서 모든 케이스에 대해 명시적으로 작성하다보니 코드가 좀 불필요하게 길긴 한데, 그렇다고 성능이 나쁜건 아니니 그냥 넘어가야겠다.