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

 

4179번: 불!

문제 지훈이는 미로에서 일을 한다. 지훈이를 미로에서 탈출하도록 도와주자! 미로에서의 지훈이의 위치와 불이 붙은 위치를 감안해서 지훈이가 불에 타기전에 탈출할 수 있는지의 여부, 그리고 얼마나 빨리 탈출할 수 있는지를 결정해야한다. 지훈이와 불은 매 분마다 한칸씩 수평또는 수직으로(비스듬하게 이동하지 않는다)  이동한다.  불은 각 지점에서 네 방향으로 확산된다.  지훈이는 미로의 가장자리에 접한 공간에서 탈출할 수 있다.  지훈이와 불은 벽이 있는 공간

www.acmicpc.net

지훈이가 미로를 탈출할 수 있는 경우에는 가장 빠른 탈출시간을 출력하는 BFS문제이다.

 

 

3055번 탈출 문제(문제 링크)랑 비슷한 것 같다.

탈출 문제에서는 물이 퍼지는 시간을 먼저 BFS로 저장해놓고, 고슴도치가 이동할 곳의 시간이 물이 퍼진시간보다 작은 경우에만 이동할 수 있도록 구현했다. (문제 풀이 링크)

 

 

이번에도 똑같이 풀어봤는데 정답은 뜨지만 메모리를 많이 써서 다른 방법으로 다시 풀어봤다.

먼저 지훈이가 이동할 좌표와 불이 확산될 좌표를 각각 다른 큐에 넣어놓고 이동시킨다.

그리고 매분마다 불을 먼저 이동시키고 지훈이를 이동시키면 불이 확산되지 않은 곳으로 이동할 수 있다.

 

 

(입력받을 때 지훈이의 위치는 지나갈 수 있는 공간이므로 '.'으로 바꿔줘야 한다.)

 

 

미로를 탈출하는 경우는 입력으로 주어지는 지도 밖으로 벗어나는 경우이므로

배열의 크기를 더 크게 잡아서 범위를 벗어나는 경우에 탈출한 것으로 본다.

아래 그림처럼 지도를 (1, 1)부터 입력받으면 지훈이의 위치가 0행이거나 0열이거나 R+1행이거나 C+1 열인 경우 미로를 탈출한다.

 

 

BFS를 진행할 때에는, 먼저 지훈이가 이동할 수 있는 곳이 더 없다면 끝나기 때문에 지훈이의 좌표를 담은 큐가 비었다면 BFS를 종료한다. 그리고 매분마다 불을 먼저 확산시키고 다음에 지훈이를 이동시키기 위해서

각자의 큐사이즈만큼씩만 진행하면 된다.

한 번에 이동할 수 있는 위치만큼(현재 위치로부터 1만큼 이동한 곳)이 큐에 새로 들어가기 때문이다.

 

 

지훈이가 미로를 탈출했다면 그때의 시간을 출력하고 바로 리턴해서 종료시킨다.

이 부분에서 종료되지 않고 BFS탐색을 끝낸다면 탈출이 불가능한 경우이므로 IMPOSSIBLE을 출력하고 종료한다.

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
#include <iostream>
#include <queue>
using namespace std;
 
int r, c;
char map[1001][1001];
bool visit[1001][1001];
int dx[] = { 0,0,1,-1 };
int dy[] = { 1,-1,0,0 };
 
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    //불이 이동할 큐
    queue<pair<intint> > fq;
    //지훈이 이동할 큐
    queue<pair<intint> > jq;  
    
 
    cin >> r >> c;
    for (int i = 1; i <= r; i++) {
        for (int j = 1; j <= c; j++) {
            cin >> map[i][j];
 
            if (map[i][j] == 'J') {
                jq.push(make_pair(i, j));
                visit[i][j] = true;
 
                map[i][j] = '.';
            }
            else if (map[i][j] == 'F') {
                fq.push(make_pair(i, j));
                visit[i][j] = true;
            }
        }
    }
 
 
    int x, y;
    int time = 0;
    while (!jq.empty()) {
        int jqsize = jq.size();
        int fqsize = fq.size();
        int nx, ny;
 
        time++;
 
        //불이 먼저 이동
        while (fqsize--) {
            x = fq.front().first;
            y = fq.front().second;
            fq.pop();
 
            for (int k = 0; k < 4; k++) {
                nx = x + dx[k];
                ny = y + dy[k];
                
                //범위 체크
                if (nx <= 0 || ny <= 0 || nx > r || ny > c) continue;
                //벽이면 못간다
                if (map[nx][ny] == '#'continue;
                //방문 체크
                if (visit[nx][ny]) continue;
 
 
                fq.push(make_pair(nx, ny));
                visit[nx][ny] = true;
            }
        }
 
 
        //지훈이 이동
        while (jqsize--) {
            x = jq.front().first;
            y = jq.front().second;
            jq.pop();
 
            for (int k = 0; k < 4; k++) {
                nx = x + dx[k];
                ny = y + dy[k];
 
                //벽이면 못간다
                if (map[nx][ny] == '#'continue;
                //이미 이동한 곳이거나,  불이 이미 확산되어 있거나 같은 시간에 확산된다
                if (visit[nx][ny]) continue;
                
                //탈출한 경우 바로 종료
                if (nx == 0 || ny == 0 || nx == r + 1 || ny == c + 1) {
                    cout << time << '\n';
                    return 0;
                }
 
                jq.push(make_pair(nx, ny));
                visit[nx][ny] = true;
            }
        }
 
 
    }
 
 
    //위에서 종료되지 못한 경우
    cout << "IMPOSSIBLE" << '\n';
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 10757. 큰 수 A + B  (0) 2020.03.10
[BOJ] 1331. 나이트 투어  (0) 2020.03.03
[BOJ] 1938. 통나무 옮기기  (0) 2020.02.12
[BOJ] 17780. 새로운 게임  (0) 2020.02.03
[BOJ] 17837. 새로운 게임 2  (0) 2020.02.03

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

 

1938번: 통나무 옮기기

첫째 줄에 주어진 평지의 한 변의 길이 N이 주어진다. (4<=N<=50) 주어진다. 이어서 그 지형의 정보가 0, 1, B, E로 이루어진 문자열로 주어진다. 한 줄에 입력되는 문자열의 길이는 N이며 입력 문자 사이에는 빈칸이 없다. 통나무와 최종 위치의 개수는 1개이다.

www.acmicpc.net

처음 주어진 통나무의 위치로부터 가능한 동작(U, D, R, L, T)을 모두 사용하여 도착지점까지 이동하면 된다.

이동할 때마다 매번 U, D, R, D, T를 해보고 이동이 가능하다면 동작수를 증가시켜서 이동하면 된다.

 

 

 

이동했을 때 범위를 벗어나거나 / 잘려지지 않은 나무가 있는 경우가 아니라면 이동 가능하다.

최소 동작수를 구해야 하므로 이미 갔던 좌표에도 다시 갈 필요가 없다.

하지만 통나무의 방향이 가로방향으로 온 것과 세로 방향으로 온 것은 구분해주어야 한다.

특히, 회전(T)을 할 때에는 3X3 정사각형 위치에 나무가 있거나 범위를 벗어나지 않는지 모두 확인해주어야 한다.

 

 

 

처음 입력받을 때 처음 통나무 위치의 좌표( pair<intint> start[3] )

이동할 위치의 좌표( pair<intint> dest[3] )를 각각 배열에 저장한다.

그리고 실제 이동은 통나무의 길이는 3으로 정해져 있으므로 중심점의 좌표만 이용하여 이동한다.

 

처음 방향은 이어진 두 좌표가 같은 x좌표라면 (같은 행에 있다면) 가로 방향이고

그렇지 않다면(다른 x좌표 == 다른 행) 세로 방향이다.

 중심점의 좌표와 방향을 이용하여 통나무를 이동시킨다.

 

 

 

이동할 통나무의 정보는 Tree라는 구조체를 만들어서 저장하였다.

Tree는 x좌표와 y좌표 dir(방향), cnt(동작수)를 가지고 있다.

방향은 0이면 가로방향 1이면 세로 방향으로 해놓았다.

이동이 가능할 때마다 이동할 중심 좌표의 위치와, 방향, 이전 동작수 +1을 해서 새로 큐에 넣어주면 된다.

 

 

 

최소 동작 수로 이동시키기 위해서 BFS를 사용하는데

방향이 가로방향인지 세로 방향인지에 따라 이동할 위치를 검사할 조건이 다르므로 각각 구현해준다.

이동한 위치의 중심 좌표가 목표한 지점의 중심 좌표와 같고 방향도 같다면 목표지점에 도착한 것이므로 그때의 동작수가 정답이다.

 
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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#include <iostream>
#include <queue>
using namespace std;
 
int n;
char map[50][50];
 
pair<intint> start[3];
pair<intint> dest[3];
 
//도착지점의 방향
int destDir;
 
int dx[] = { -1,-1,-1,0,0,1,1,1 };
int dy[] = { -1,0,1,-1,1,-1,0,1 };
 
 
//visit[x][y][0] = 가로방향인 상태에서 x,y 에 방문
//visit[x][y][1] = 세로방향인 상태에서 x,y에 방문
bool visit[50][50][2];
 
 
struct Tree {
    int x, y;
    int dir;
    int cnt;
    Tree(int i, int j, int d, int c) {
        x = i;
        y = j;
        dir = d;
        cnt = c;
    }
};
 
 
 
bool turn(int x, int y) {
 
    int nx, ny;
    //중심점에 인접한 8방향을 검사
    for (int k = 0; k < 8; k++) {
        nx = x + dx[k];
        ny = y + dy[k];
 
        //범위를 벗어나면 회전 불가능
        if (nx < 0 || ny < 0 || nx >= n || ny >= n) return false;
        //나무 있으면 회전 불가능
        if (map[nx][ny] == '1'return false;
    }
 
 
    //위의 조건에서 걸리지 않으면 회전 가능
    return true;
}
 
 
 
int solve(int x, int y, int dir) {
    queue<Tree> q;
 
      //x좌표, y좌표, 방향, 동작수
    q.push(Tree(x, y, dir, 0));
    visit[x][y][dir] = true;
 
 
    int cnt;
    while (!q.empty()) {
        x = q.front().x;
        y = q.front().y;
        dir = q.front().dir;
        cnt = q.front().cnt;
        q.pop();
 
 
        //도착지점의 중심좌표와 방향이 같다.
        if (x == dest[1].first && y == dest[1].second && dir == destDir) return cnt;
 
 
        int nx, ny;
        if (dir == 0) {
            //통나무의 현재 방향이 가로방향
 
            //U
            nx = x - 1;
            ny = y;
            //범위와 방문 체크
            if (nx >= 0 && !visit[nx][ny][dir]) {
                //이동할 위치에 나무가 있는지 체크
                if (map[nx][ny] == '0' && map[nx][ny - 1== '0' && map[nx][ny + 1== '0') {
                    visit[nx][ny][dir] = true;
                    q.push(Tree(nx, ny, dir, cnt + 1));
                }
            }
 
 
            //D
            nx = x + 1;
            ny = y;
            if (nx < n && !visit[nx][ny][dir]) {
                if (map[nx][ny] == '0' && map[nx][ny - 1== '0' && map[nx][ny + 1== '0') {
                    visit[nx][ny][dir] = true;
                    q.push(Tree(nx, ny, dir, cnt + 1));
                }
            }
 
 
            //L
            nx = x;
            ny = y - 1;
            if (ny -1 >= 0  && !visit[nx][ny][dir]) {
                if (map[nx][ny - 1== '0') {
                    visit[nx][ny][dir] = true;
                    q.push(Tree(nx, ny, dir, cnt + 1));
                }
            }
 
 
            //R
            nx = x;
            ny = y + 1;
            if (ny + 1 < n && !visit[nx][ny][dir]) {
                if (map[nx][ny + 1== '0') {
                    visit[nx][ny][dir] = true;
                    q.push(Tree(nx, ny, dir, cnt + 1));
                }
            }
 
 
            //T
            if (turn(x,y) && !visit[x][y][1]) {
                q.push(Tree(x, y, 1, cnt + 1));
                visit[x][y][1= true;
            }
 
        }
        else {
            //세로 방향
 
            //U
            nx = x - 1;
            ny = y;
            if (nx - 1 >= 0 && !visit[nx][ny][dir]) {
                if (map[nx - 1][ny] == '0') {
                    q.push(Tree(nx, ny, dir, cnt + 1));
                    visit[nx][ny][dir] = true;
                }
            }
 
 
            //D
            nx = x + 1;
            ny = y;
            if (nx + 1 < n && !visit[nx][ny][dir]) {
                if (map[nx + 1][ny] == '0') {
                    q.push(Tree(nx, ny, dir, cnt + 1));
                    visit[nx][ny][dir] = true;
                }
            }
 
 
            //L
            nx = x;
            ny = y - 1;
            if (ny >= 0 && !visit[nx][ny][dir]) {
                if (map[nx - 1][ny] == '0' && map[nx][ny] == '0' && map[nx + 1][ny] == '0') {
                    q.push(Tree(nx, ny, dir, cnt + 1));
                    visit[nx][ny][dir] = true;
                }
            }
 
 
            //R
            nx = x;
            ny = y + 1;
            if (ny < n && !visit[nx][ny][dir]) {
                if (map[nx - 1][ny] == '0' && map[nx][ny] == '0' && map[nx + 1][ny] == '0') {
                    q.push(Tree(nx, ny, dir, cnt + 1));
                    visit[nx][ny][dir] = true;
                }
            }
 
 
            //T
            if (turn(x, y) && !visit[x][y][0]) {
                q.push(Tree(x, y, 0, cnt + 1));
                visit[x][y][0= true;
            }
        }
 
    }
 
    return 0;
}
 
 
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    cin >> n;
 
 
    int k = 0;
    int l = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cin >> map[i][j];
            if (map[i][j] == 'B') {
                start[k].first = i;
                start[k].second = j;
                k++;
                map[i][j] = '0';
            }
            else if (map[i][j] == 'E') {
                dest[l].first = i;
                dest[l].second = j;
                l++;
                map[i][j] = '0';
            }
        }
    }
 
 
    //도착지점의 방향
    //첫번째와 두번째 위치의 x좌표가 같으면 가로방향
    //가로 방향 : 0 , 세로 방향 : 1
    if (dest[0].first == dest[1].first) destDir = 0;
    else destDir = 1;
 
 
 
    //시작위치의 방향
    //첫번째와 두번째 위치의 x좌표가 같으면 가로방향
    //가로 방향 : 0 , 세로 방향 : 1
    if (start[0].first == start[1].first) cout << solve(start[1].first, start[1].second, 0);
    else cout << solve(start[1].first, start[1].second, 1);
 
 
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 1331. 나이트 투어  (0) 2020.03.03
[BOJ] 4179. 불!  (0) 2020.02.17
[BOJ] 17780. 새로운 게임  (0) 2020.02.03
[BOJ] 17837. 새로운 게임 2  (0) 2020.02.03
[BOJ] 1600. 말이 되고픈 원숭이  (0) 2020.02.01

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

 

2234번: 성곽

문제 대략 위의 그림과 같이 생긴 성곽이 있다. 굵은 선은 벽을 나타내고, 점선은 벽이 없어서 지나다닐 수 있는 통로를 나타낸다. 이러한 형태의 성의 지도를 입력받아서 다음을 계산하는 프로그램을 작성하시오. 이 성에 있는 방의 개수 가장 넓은 방의 넓이 하나의 벽을 제거하여 얻을 수 있는 가장 넓은 방의 크기 위의 예에서는 방은 5개고, 가장 큰 방은 9개의 칸으로 이루어져 있으며, 위의 그림에서 화살표가 가리키는 벽을 제거하면 16인 크기의 방을 얻을

www.acmicpc.net

  1. 이 성에 있는 방의 개수
  2. 가장 넓은 방의 넓이
  3. 하나의 벽을 제거하여 얻을 수 있는 가장 넓은 방의 크기

를 구하는 문제이다. 이 문제는 bfs와 비트 마스크를 사용해서 풀었다.

 

 

먼저 1번과 2번문제에 대한 답을 먼저 구하였다.

방문하지 않은 모든 칸에 bfs를 사용하여 방문하고 bfs가 시작될 때마다(새로운 방이므로) 방 개수를 늘려준다.

그리고 bfs탐색을 할때마다 방문하는 칸의 개수를 세줘서 방의 넓이를 구하여 방의 넓이 중 최댓값을 구할 수 있다.

 

이 문제에서 bfs를 할 때 중요한 부분은 벽인지 아닌지 알아내야 한다.

 

 

벽에 대한 정보는 한 정수로 주어지는데, 서쪽에 벽이 있을 때는 1을, 북쪽에 벽이 있을 때는 2를, 동쪽에 벽이 있을 때는 4를, 남쪽에 벽이 있을 때는 8을 더한 값이 주어진다. 참고로 이진수의 각 비트를 생각하면 쉽다. 따라서 이 값은 0부터 15까지의 범위 안에 있다.

라고 문제 입력에서 주어지는데 이 부분이 힌트인 것 같다. 그림으로 나타내면 아래와 같다.

 

 

 

 

문제에서 주어진 예시 그림(맨 위 그림)의 왼쪽 끝칸(0,0)을 예시로 들어보면 아래 그림과 같다. 서쪽, 북쪽, 남쪽에 벽이 있기 때문에

1+2+8 = 11로 11이 문제 입력에서 주어진다. 11은 다시 이진수로 1011로 나타낼 수 있으므로 이를 이용해서 비트 마스크로 검사를 한다.

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

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

먼저 방향 배열을 서, 북, 동, 남 순서로 해놓아야 한다. 그래야 1 << 0을 했을 때의 값이 1이므로 서쪽을 표현할 수 있고 1<<1 은 2로 북쪽을 표현할 수 있다.

그리고 검사할 칸과 & (1<<k)를 해서 해당 위치에 1을 가지고 있는지 검사한다. (k는 0부터 3으로 네 방향의 index)

1을 가지고 있는 경우(위의 연산 결과가 0이 아닌 경우)에는 벽이므로 해당 방향으로는 이동하지 않는다.

 

 

그리고 나중에 3번 문제를 해결하기 위해서플러드 필 알고리즘(연결 요소에 번호를 매김)을 사용한다. bfs를 할 때 현재 연결된 칸들에 방 번호를 매겨주고 각 방의 넓이 또한 cnt 배열에 저장해놓는다.

 

 

3번 문제를 해결하기 위해서는 다음과 같은 생각을 했다.

인접해있는 방과 현재 방의 넓이를 합쳤을 때의 최댓값이 하나의 벽을 제거했을 때의 결과와 마찬가지다.

아래 그림을 봤을 때 1번 방은 2번 방과 3번 방과 인접해있는데 3번 방과 인접해있는 벽 중 하나를 제거했을 때 최댓값을 얻을 수 있다.

 

 

코드로 구현하자면 다시 새로운 bfs를 이용한다. 이때 bfs에서는 아까 플러드 필 알고리즘을 사용하여 방 번호를 매긴 배열을 이용한다.

이동했을 때 같은 방인 경우에는 계속 큐에 넣고 방문 체크를 해주며 탐색을 이어가고 같은 방이 아닌 경우에는 현재 방의 칸수와 옆의 방의 칸수를 합친 값(위에서 저장한 cnt배열을 이용)을 저장하고 이 합쳐진 값들 중 최댓값이 정답이 된다.

 

1번 방에 대해 인접한 방에 대해서 모두 탐색을 해봤다면 다음 2번 방을 검사할 때는 다시 1번 방과 비교할 필요는 없다.

그러므로 bfs로 방문한 칸은 모두 다 체크해주면 된다.

 

 

역시 코드를 보는 게 가장 이해하기 쉬울 것 같다ㅎㅎ

순서는 main -> solve -> bfs 다음에 removeWall -> bfs2로 진행된다.

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
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
 
int m, n;
int map[50][50];
int visit[50][50];
bool check[50][50];
int cnt[2500];
 
 
int dx[] = { 0,-1,0,1 };
int dy[] = { -1,0,1,0 };
 
int bfs2(int x, int y) {
    queue<pair<intint>> q;
    q.push(make_pair(x, y));
    check[x][y] = true;
 
    int sum = 0;
    while (!q.empty()) {
        x = q.front().first;
        y = q.front().second;
        q.pop();
 
        for (int k = 0; k < 4; k++) {
            int nx = x + dx[k];
            int ny = y + dy[k];
            if (nx < 0 || ny < 0 || nx >= n || ny >= m) continue;
            if (check[nx][ny]) continue;
 
 
            //같은 방이면 큐에 넣고
            if (visit[nx][ny] == visit[x][y]) {
                q.push(make_pair(nx, ny));
                check[nx][ny] = true;
            }
            else { //다른 방이면 그 방과 합쳐졌을 때의 넓이를 저장
                int tmp = cnt[visit[x][y]] + cnt[visit[nx][ny]];
                if (sum < tmp) sum = tmp;
            }
 
        }
    }
 
    return sum;
 
}
 
void removeWall() {
    queue<pair<intint>> q;
 
    int sum = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (check[i][j]) continue;
            int tmp = bfs2(i, j);
            if (sum < tmp) sum = tmp;
        }
    }
    
    cout << sum << '\n';
}
 
int bfs(int x, int y, int num) {
    int cnt = 1;
    queue<pair<intint>> q;
    q.push(make_pair(x, y));
    visit[x][y] = num;
 
    while (!q.empty()) {
        x = q.front().first;
        y = q.front().second;
        q.pop();
 
        for (int k = 0; k < 4; k++) {
            //해당 방향이 벽인 경우
            if (map[x][y] & (1 << k)) continue;
 
            int nx = x + dx[k];
            int ny = y + dy[k];
 
            if (nx < 0 || ny < 0 || nx >= n || ny >= m) continue;
            if (visit[nx][ny]) continue;
 
            q.push(make_pair(nx, ny));
 
            //방 번호를 기록
            visit[nx][ny] = num;
            cnt++;
        }
 
    }
 
 
    //연결된 칸의 개수를 리턴(방의 넓이)
    return cnt;
}
 
void solve() {
 
    int maxcnt = 0;
    int roomcnt = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (visit[i][j]) continue;
 
            //bfs출발할 때마다 방의 개수 늘어난다
            int tmp = bfs(i, j, ++roomcnt);
 
            //방 별로 넓이를 저장해놓는다.
            cnt[roomcnt] = tmp;
 
            //가장 넓은 방의 넓이
            if (maxcnt < tmp) maxcnt = tmp;
        }
    }
 
    //1번 답 출력
    cout << roomcnt << "\n";
 
    //2번 답 출력
    cout << maxcnt << "\n";
 
 
 
}
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    cin >> m >> n;
 
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> map[i][j];
        }
    }
 
    //1번과 2번 답을 구한다
    solve();
 
    //3번 답을 구한다
    removeWall();
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 2468. 안전 영역  (0) 2019.08.13
[BOJ] 2573. 빙산  (0) 2019.08.13
[BOJ] 11559. Puyo Puyo  (0) 2019.08.08
[BOJ] 3190. 뱀  (0) 2019.08.06
[BOJ] 15683. 감시  (0) 2019.08.05

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

 

11724번: 연결 요소의 개수

첫째 줄에 정점의 개수 N과 간선의 개수 M이 주어진다. (1 ≤ N ≤ 1,000, 0 ≤ M ≤ N×(N-1)/2) 둘째 줄부터 M개의 줄에 간선의 양 끝점 u와 v가 주어진다. (1 ≤ u, v ≤ N, u ≠ v) 같은 간선은 한 번만 주어진다.

www.acmicpc.net

제목대로 연결 요소의 개수를 구하는 문제인데 배열 형태가 아니므로 각 정점을 인접 리스트로 구현해준다.

나는 int형 벡터를 갖는 배열로 구현하였다.

간선의 개수만큼 반복문을 실행하여 정점을 입력받아 u리스트에는 v를 추가하고 v리스트에는 u를 추가한다.

 

한 번 bfs를 시작할때 연결된 정점을 모두 방문한다. 즉, bfs가 한번 도는게 연결 요소 1개인 셈이다.

그러므로 모든 방문하지 않은 정점을 방문해서 bfs가 새로 실행될 때마다 ans값을 증가시켜주면 된다.

 

 

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
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
 
bool check[1000];
vector<int> list[1000];
 
void bfs(int x) {
    queue<int> q;
    q.push(x);
    check[x] = true;
 
    while(!q.empty()) {
        x = q.front();
        q.pop();
 
 
        //연결된 정점들을 검사
        for(int k=0; k<list[x].size(); k++) {
            int y = list[x][k];
            if(check[y] == false) {
                q.push(y);
                check[y] = true;
            }
        }
    }
}
 
int main() {
 
    int n,m;
    scanf("%d %d"&n,&m);
 
 
    //인접 리스트(벡터 배열로 구현)
    for(int i=0; i<m; i++) {
        int u, v;
        scanf("%d %d"&u,&v);
        list[u].push_back(v);
        list[v].push_back(u);
    }
 
    int ans = 0;
    for(int i=1; i<=n; i++) {
        if(check[i] == false) {
            ++ans;
            bfs(i);
        }
    }
 
    printf("%d", ans);
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 7569. 토마토 (3차원)  (0) 2019.06.27
[BOJ] 7576. 토마토  (0) 2019.06.27
[BOJ] 2178. 미로 탐색  (0) 2019.06.25
[BOJ] 4963. 섬의 개수  (0) 2019.06.25
[BOJ] 2667. 단지번호붙이기  (0) 2019.06.25

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

 

2178번: 미로 탐색

첫째 줄에 두 정수 N, M(2 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 M개의 정수로 미로가 주어진다. 각각의 수들은 붙어서 입력으로 주어진다.

www.acmicpc.net

이 문제는 (1, 1)에서 출발하여 (N, M)의 위치로 이동할 때 지나야 하는 최소의 칸 수를 구하는 문제이다.

문제에서는 1, 1이라고 한 곳은 실제로는 0, 0 이므로 주의해야 한다!

마찬가지로 마지막 칸 N, M도 (N-1, M-1) 칸을 출력해주면 된다.

 

최소 칸수를 구하는 문제이므로 bfs를 사용한다.

시작칸에서 부터 갈 수 있는 칸으로 단계적으로 진행하기 때문에 최단 거리로 이동할 수 있다.

이동할 때마다 각 칸에는 해당 칸까지의 이동 횟수를 저장해준다.

그러면 최종적으로 마지막 칸에 마지막 칸까지의 최소 이동 횟수가 저장된다.

 

 

문제에 나와있는 예시를 보면 아래와 같이 지도가 있는데

1 0 1 1 1 1
1 0 1 0 1 0
1 0 1 0 1 1
1 1 1 0 1 1

 

bfs를 통해 이동 횟수를 cnt배열에 저장해주면 다음과 같다.

1 0 9 10 11 12
2 0 8 0 12 0
3 0 7 0 13 14
4 5 6 0 14 15

 

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
#include <cstdio>
#include <queue>
using namespace std;
 
int n,m;
int map[100][100];
int cnt[100][100];
int dx[] = {0,0,1,-1};
int dy[] = {1,-1,0,0};
 
int main() {
    scanf("%d %d"&n,&m);
 
    for(int i=0; i<n; i++) {
        for(int j=0; j<m; j++) {
            scanf("%1d"&map[i][j]);
        }
    }
 
    queue<pair<int,int> > q;
 
    //첫번째 칸에서 시작
    q.push(make_pair(0,0));
    cnt[0][0= 1;
 
    while(!q.empty()) {
        int x = q.front().first;
        int y = q.front().second;
        q.pop();
        for(int k=0; k<4; k++) {
            int nx = x + dx[k];
            int ny = y + dy[k];
            if(nx < 0 || ny < 0 || nx >= n || ny >= m) continue;
            if(map[nx][ny] == 1 && cnt[nx][ny] == 0) {
                q.push(make_pair(nx,ny));
 
                //이전 단계보다 1 증가(이동 횟수 증가)
                cnt[nx][ny] = cnt[x][y] + 1;
            }
        }
    }
 
    //마지막 칸을 출력
    printf("%d\n", cnt[n-1][m-1]);
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 7576. 토마토  (0) 2019.06.27
[BOJ] 11724. 연결 요소의 개수  (0) 2019.06.27
[BOJ] 4963. 섬의 개수  (0) 2019.06.25
[BOJ] 2667. 단지번호붙이기  (0) 2019.06.25
[BOJ] 14888. 연산자 끼워넣기  (0) 2019.06.20

+ Recent posts