https://www.acmicpc.net/problem/2583
영역 구하기
문제
눈금의 간격이 1인 M×N(M,N≤100)크기의 모눈종이가 있다. 이 모눈종이 위에 눈금에 맞추어 K개의 직사각형을 그릴 때, 이들 K개의 직사각형의 내부를 제외한 나머지 부분이 몇 개의 분리된 영역으로 나누어진다.
예를 들어 M=5, N=7 인 모눈종이 위에 <그림 1>과 같이 직사각형 3개를 그렸다면, 그 나머지 영역은 <그림 2>와 같이 3개의 분리된 영역으로 나누어지게 된다.

<그림 2>와 같이 분리된 세 영역의 넓이는 각각 1, 7, 13이 된다.
M, N과 K 그리고 K개의 직사각형의 좌표가 주어질 때, K개의 직사각형 내부를 제외한 나머지 부분이 몇 개의 분리된 영역으로 나누어지는지, 그리고 분리된 각 영역의 넓이가 얼마인지를 구하여 이를 출력하는 프로그램을 작성하시오.
입력
첫째 줄에 M과 N, 그리고 K가 빈칸을 사이에 두고 차례로 주어진다. M, N, K는 모두 100 이하의 자연수이다. 둘째 줄부터 K개의 줄에는 한 줄에 하나씩 직사각형의 왼쪽 아래 꼭짓점의 x, y좌표값과 오른쪽 위 꼭짓점의 x, y좌표값이 빈칸을 사이에 두고 차례로 주어진다. 모눈종이의 왼쪽 아래 꼭짓점의 좌표는 (0,0)이고, 오른쪽 위 꼭짓점의 좌표는(N,M)이다. 입력되는 K개의 직사각형들이 모눈종이 전체를 채우는 경우는 없다.
출력
첫째 줄에 분리되어 나누어지는 영역의 개수를 출력한다. 둘째 줄에는 각 영역의 넓이를 오름차순으로 정렬하여 빈칸을 사이에 두고 출력한다.
예제 입력 1 복사
5 7 3
0 2 4 4
1 1 2 5
4 0 6 2
예제 출력 1 복사
3
1 7 13
풀이
import java.io.*;
import java.util.*;
public class Main {
static int[][] A;
static boolean[][] visited;
static int M,N;
static int[] dx = {-1, 1, 0, 0};
static int[] dy = {0, 0, -1, 1};
public static void main(String[] args)throws IOException {
BufferedReader br= new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
M = Integer.parseInt(st.nextToken());
N = Integer.parseInt(st.nextToken());
int K = Integer.parseInt(st.nextToken());
A = new int[M][N];
visited = new boolean[M][N];
for (int i = 0; i < M; i++) {
Arrays.fill(A[i], 1);
}
// 직사각형 그리기
while (K-- > 0){
st = new StringTokenizer(br.readLine());
// 왼쪽아래 좌표
int x1 = Integer.parseInt(st.nextToken());
int y1 = Integer.parseInt(st.nextToken());
// 오른쪽 위 좌표
int x2 = Integer.parseInt(st.nextToken());
int y2 = Integer.parseInt(st.nextToken());
for (int i = x1; i < x2; i++) {
for (int j = y1; j < y2; j++) {
A[j][i] = 0;
}
}
}
int count = 0; // bfs 횟수 (영역개수)
ArrayList<Integer> spacious = new ArrayList<>(); // 영역당 넓이
for (int i=0; i<M; i++){
for (int j=0; j<N; j++){
if (!visited[i][j] && A[i][j]!=0){
spacious.add(bfs(i,j));
count++;
}
}
}
Collections.sort(spacious);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
bw.write(count + "\n");
for (int i=0; i<spacious.size(); i++){
bw.write(spacious.get(i) + " ");
}
bw.flush();
bw.close();
}
private static int bfs(int i, int j){
visited[i][j] = true; // 방문처리
Queue<int[]> q = new LinkedList<>();
q.offer(new int[] {i, j});
int spacious = 0;
while (!q.isEmpty()){
int[] now = q.poll();
spacious++;
for(int k=0; k<4; k++){
int nx = now[0] + dx[k];
int ny = now[1] + dy[k];
if (isIn(nx, ny)&& !visited[nx][ny]){
visited[nx][ny] = true;
if (A[nx][ny] != 0){
q.offer(new int[] {nx, ny});
}
}
}
}
return spacious;
}
private static boolean isIn(int x, int y){
return x>=0 && y>=0 && x<M && y<N;
}
}
- BFS를 활용한 문제
- MxN크기의 2차원 배열을 선언한 후, 모두 1로 채워준다.
직사각형 칠해주기
// 직사각형 그리기
while (K-- > 0){
st = new StringTokenizer(br.readLine());
// 왼쪽아래 좌표
int x1 = Integer.parseInt(st.nextToken());
int y1 = Integer.parseInt(st.nextToken());
// 오른쪽 위 좌표
int x2 = Integer.parseInt(st.nextToken());
int y2 = Integer.parseInt(st.nextToken());
for (int i = x1; i < x2; i++) {
for (int j = y1; j < y2; j++) {
A[j][i] = 0;
}
}
- 칠하고자 하는 직사각형의 왼쪽아래 좌표, 오른쪽 위 좌표를 입력받아서 해당영역을 0으로 채워준다.
영역개수와 영역 넓이 구하기
int count = 0; // bfs 횟수 (영역개수)
ArrayList<Integer> spacious = new ArrayList<>(); // 영역당 넓이
for (int i=0; i<M; i++){
for (int j=0; j<N; j++){
if (!visited[i][j] && A[i][j]!=0){
spacious.add(bfs(i,j));
count++;
}
}
}
Collections.sort(spacious);
- bfs 수행횟수는 영역의 갯수이다.
- bfs를 한번 수행할 때마다 한 영역의 넓이를 return 하므로 spacious 리스트에 저장해준다.
- bfs 수행이 모두 완료되면, spacious 리스트를 오름차순으로 정렬해준다.
BFS (영역 넓이 탐색)
private static int bfs(int i, int j){
visited[i][j] = true; // 방문처리
Queue<int[]> q = new LinkedList<>();
q.offer(new int[] {i, j});
int spacious = 0;
while (!q.isEmpty()){
int[] now = q.poll();
spacious++;
for(int k=0; k<4; k++){
int nx = now[0] + dx[k];
int ny = now[1] + dy[k];
if (isIn(nx, ny)&& !visited[nx][ny]){
visited[nx][ny] = true;
if (A[nx][ny] != 0){
q.offer(new int[] {nx, ny});
}
}
}
}
return spacious;
}
private static boolean isIn(int x, int y){
return x>=0 && y>=0 && x<M && y<N;
}
- 현재 좌표의 상하좌우 좌표를 탐색하면서, 아직 방문하지 않았고 해당영역이 1이라면 큐에 넣는다.
- 큐에서 현재좌표를 poll할때마다 spacious를 1증가시켜준다. 어차피 한칸당 넓이가 1이므로 spacious++ 해주면 된다.
- 상하좌우 탐색좌표가 유효한 좌표인지 구분하기 위해서 isIn이라는 함수를 같이 선언해주었다.
'코딩테스트 > 백준' 카테고리의 다른 글
[백준 14888] 연산자 끼워넣기 (Java) - 브루트포스, DFS, 백트래킹 (0) | 2025.04.27 |
---|---|
[백준 24479] 알고리즘 수업 - 깊이 우선 탐색 1 (Java) - DFS (0) | 2025.04.27 |
[백준 16922] 로마 숫자 만들기 (Java) - DFS, 백트래킹 (0) | 2025.04.25 |
[백준 2146] 다리 만들기(Java) - BFS (0) | 2025.04.24 |
[백준 1316] 그룹 단어 체커 (Java) - 구현 (0) | 2025.04.23 |