2234번 성곽
문제
대략 위의 그림과 같이 생긴 성곽이 있다. 굵은 선은 벽을 나타내고, 점선은 벽이 없어서 지나다닐 수 있는 통로를 나타낸다. 이러한 형태의 성의 지도를 입력받아서 다음을 계산하는 프로그램을 작성하시오.
- 이 성에 있는 방의 개수
- 가장 넓은 방의 넓이
- 하나의 벽을 제거하여 얻을 수 있는 가장 넓은 방의 크기
위의 예에서는 방은 5개고, 가장 큰 방은 9개의 칸으로 이루어져 있으며, 위의 그림에서 화살표가 가리키는 벽을 제거하면 16인 크기의 방을 얻을 수 있다.
성은 m×n(1 ≤ m, n ≤ 50)개의 정사각형 칸으로 이루어진다. 성에는 최소 두 개의 방이 있어서, 항상 하나의 벽을 제거하여 두 방을 합치는 경우가 있다.
입력
첫째 줄에 두 정수 n, m이 주어진다. 다음 m개의 줄에는 n개의 정수로 벽에 대한 정보가 주어진다. 벽에 대한 정보는 한 정수로 주어지는데, 서쪽에 벽이 있을 때는 1을, 북쪽에 벽이 있을 때는 2를, 동쪽에 벽이 있을 때는 4를, 남쪽에 벽이 있을 때는 8을 더한 값이 주어진다. 참고로 이진수의 각 비트를 생각하면 쉽다. 따라서 이 값은 0부터 15까지의 범위 안에 있다.
출력
첫째 줄에 1의 답을, 둘째 줄에 2의 답을, 셋째 줄에 3의 답을 출력한다.
예제 입력
7 4
11 6 11 6 3 10 6
7 9 6 13 5 15 5
1 10 12 7 13 7 5
13 11 10 8 10 12 13
예제 출력
5
9
16
해결방법
Flood Fill 을 통해 영역을 구하는 문제에 특정 조건을 부여한 BFS 탐색 문제이다
시간 복잡도는 영역을 구하는데
n^2
, 벽을 부숴보는데n^2 * 4
의 시간이 발생한다따라서
12,500
이라는 시간이 들게된다
⭐️ 솔루션 ⭐️
모든 맵을 돌며 방번호가 부여되지 않은 노드를 대상으로 BFS 탐색을 수행한다
- 방번호를 배정하면서 넓이의 값을 구한다
- 방번호 지정을 끝낼 때 마다 방 넓이를 저장하고 넓이의 최대값을 갱신시킨다
모든 맵을 돌며 주변의 벽이 존재하고 벽너머의 방의 번호가 다를 경우 벽을 부숴본다
- 부쉈을 때 합쳐지게 되는 방의 넓이를 더하여 최대값을 갱신시킨다
또한 벽의 유무를 체크하기 위해 비트 마스킹 기법을 사용했다
for(int i=0; i<4; i++){
int nx = x + dx[i];
int ny = y + dy[i];
if(nx<0 || ny<0 || nx>= n || ny>=m) continue;
if(room[nx][ny] != 0) continue;
// 해당 방향에 벽이 존재하지 않는다면
if((map[x][y] & (1<<i)) == 0){
q.push(node(nx,ny));
room[nx][ny] = cnt;
res += 1;
}
}
소스코드
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <cstring>
#include <set>
#include <queue>
#include <vector>
#include <algorithm>
#define MAX 51
#define INF 987654321
using namespace std;
typedef pair<int, int> node;
int n,m;
int res,cnt,max_area1,max_area2;
int map[MAX][MAX];
int room[MAX][MAX];
int room_area[MAX*MAX];
// 서 북 동 남
int dx[4] = {0,-1,0,1};
int dy[4] = {-1,0,1,0};
queue<node> q;
// 방의 번호를 붙이기 위한 BFS 탐색
void bfs(int r,int c){
// 배정할 방의 번호, 방의 넓이
cnt += 1;
res = 0;
// 방 번호 탐색
q.push(node(r,c));
room[r][c] = cnt;
res += 1;
while(!q.empty()){
int x = q.front().first;
int y = q.front().second;
q.pop();
for(int i=0; i<4; i++){
int nx = x + dx[i];
int ny = y + dy[i];
if(nx<0 || ny<0 || nx>= n || ny>=m) continue;
if(room[nx][ny] != 0) continue;
// 해당 방향에 벽이 존재하지 않는다면
if((map[x][y] & (1<<i)) == 0){
q.push(node(nx,ny));
room[nx][ny] = cnt;
res += 1;
}
}
}
// 방의 넓이 저장
room_area[cnt] = res;
// 최대 넓이 갱신
max_area1 = max(max_area1,res);
}
// 하나의 벽을 제거했을 때 얻을 수 있는 넓이의 최대를 구하는 탐색
void getMaxArea(int x,int y){
for(int i=0; i<4; i++){
int nx = x + dx[i];
int ny = y + dy[i];
if(nx<0 || ny<0 || nx>= n || ny>=m) continue;
// 해당 방향에 벽이 존재하고 방번호가 다르다면
if((map[x][y] & (1<<i)) && (room[x][y] != room[nx][ny])){
int sum = room_area[room[x][y]] + room_area[room[nx][ny]];
max_area2 = max(max_area2,sum);
}
}
}
int main(int argc, const char * argv[]) {
// cin,cout 속도향상
ios_base::sync_with_stdio(false);
cin.tie(NULL); cout.tie(NULL);
cin >> m >> n;
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
cin >> map[i][j];
}
}
// 방의 번호를 붙이기 위한 BFS 탐색
cnt = 0;
max_area1 = 0;
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
if(room[i][j] == 0){
bfs(i,j);
}
}
}
// 하나의 벽을 제거했을 때 얻을 수 있는 넓이의 최대를 구하는 탐색
max_area2 = 0;
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
getMaxArea(i,j);
}
}
// 1. 이 성에 있는 방의 개수
cout << cnt << "\n";
// 2. 가장 넓은 방의 넓이
cout << max_area1 << "\n";
// 3. 하나의 벽을 제거하여 얻을 수 있는 가장 넓은 방의 크기
cout << max_area2 << "\n";
return 0;
}
#include <iostream>
#include <math.h>
#include <cstring>
#include <queue>
#include <algorithm>
#define MAX 51
using namespace std;
typedef pair<int,int> node;
int n,m;
int ind,ans;
int map[MAX][MAX];
int room[MAX][MAX];
vector<int> room_size;
int dx[4] = {0,-1,0,1};
int dy[4] = {-1,0,1,0};
void bfs(int r,int c,int ind){
int res = 0;
queue<node> q;
q.push(node(r,c));
room[r][c] = ind;
res++;
while(!q.empty()){
int x = q.front().first;
int y = q.front().second;
q.pop();
for(int i=0; i<4; i++){
// 벽이 존재한다면 제외
if(map[x][y] & (1<<i)) continue;
int nx = x + dx[i];
int ny = y + dy[i];
if(room[nx][ny] == 0){
q.push(node(nx,ny));
room[nx][ny] = ind;
res++;
}
}
}
// 최대 방의 크기 갱신
room_size.push_back(res);
}
int main(int argc, const char * argv[]) {
// cin,cout 속도향상
ios_base::sync_with_stdio(false);
cin.tie(NULL); cout.tie(NULL);
cin >> m >> n;
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
cin >> map[i][j];
}
}
// 방번호 배정
ind = 0;
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
if(room[i][j]==0){
bfs(i,j,++ind);
}
}
}
// 벽을 하나 제거했을 때, 최대 방의 크기
ans = 0;
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
// 아래쪽
if(i+1 < n && room[i][j] != room[i+1][j]){
int sum = room_size[room[i][j]-1] + room_size[room[i+1][j]-1];
ans = max(ans,sum);
}
// 오른쪽
if(j+1 < m && room[i][j] != room[i][j+1]){
int sum = room_size[room[i][j]-1] + room_size[room[i][j+1]-1];
ans = max(ans,sum);
}
}
}
cout << ind << "\n";
cout << *max_element(room_size.begin(),room_size.end()) << "\n";
cout << ans << "\n";
return 0;
}
'Algorithm > BOJ 문제풀이' 카테고리의 다른 글
[BFS] 14426번 이모티콘 (0) | 2018.11.30 |
---|---|
[BFS] 3184번 양 (0) | 2018.11.29 |
[BFS] 1600번 말이 되고픈 원숭이 (0) | 2018.11.29 |
[BFS] 10026번 적록색약 (0) | 2018.11.29 |
[BFS] 14442번 벽 부수고 이동하기2 (0) | 2018.11.28 |