본문으로 바로가기

[BFS] 2234번 성곽

category Algorithm/BOJ 문제풀이 2018. 11. 29. 21:56
2234_성곽

2234번 성곽

 

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


 

문제

img

대략 위의 그림과 같이 생긴 성곽이 있다. 굵은 선은 벽을 나타내고, 점선은 벽이 없어서 지나다닐 수 있는 통로를 나타낸다. 이러한 형태의 성의 지도를 입력받아서 다음을 계산하는 프로그램을 작성하시오.

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

위의 예에서는 방은 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 이라는 시간이 들게된다

 

⭐️ 솔루션 ⭐️

  1. 모든 맵을 돌며 방번호가 부여되지 않은 노드를 대상으로 BFS 탐색을 수행한다

    • 방번호를 배정하면서 넓이의 값을 구한다
    • 방번호 지정을 끝낼 때 마다 방 넓이를 저장하고 넓이의 최대값을 갱신시킨다
  2. 모든 맵을 돌며 주변의 벽이 존재하고 벽너머의 방의 번호가 다를 경우 벽을 부숴본다

    • 부쉈을 때 합쳐지게 되는 방의 넓이를 더하여 최대값을 갱신시킨다

 

또한 벽의 유무를 체크하기 위해 비트 마스킹 기법을 사용했다

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