[백준/Java] 10026- 적록색약

승래·2026년 1월 16일

10026- 적록색약

문제 바로가기

문제

문제
적록색약은 빨간색과 초록색의 차이를 거의 느끼지 못한다. 따라서, 적록색약인 사람이 보는 그림은 아닌 사람이 보는 그림과는 좀 다를 수 있다.

크기가 N×N인 그리드의 각 칸에 R(빨강), G(초록), B(파랑) 중 하나를 색칠한 그림이 있다. 그림은 몇 개의 구역으로 나뉘어져 있는데, 구역은 같은 색으로 이루어져 있다. 또, 같은 색상이 상하좌우로 인접해 있는 경우에 두 글자는 같은 구역에 속한다. (색상의 차이를 거의 느끼지 못하는 경우도 같은 색상이라 한다)

예를 들어, 그림이 아래와 같은 경우에

RRRBB
GGBBB
BBBRR
BBRRR
RRRRR
적록색약이 아닌 사람이 봤을 때 구역의 수는 총 4개이다. (빨강 2, 파랑 1, 초록 1) 하지만, 적록색약인 사람은 구역을 3개 볼 수 있다. (빨강-초록 2, 파랑 1)

그림이 입력으로 주어졌을 때, 적록색약인 사람이 봤을 때와 아닌 사람이 봤을 때 구역의 수를 구하는 프로그램을 작성하시오.

입력
첫째 줄에 N이 주어진다. (1 ≤ N ≤ 100)

둘째 줄부터 N개 줄에는 그림이 주어진다.

출력
적록색약이 아닌 사람이 봤을 때의 구역의 개수와 적록색약인 사람이 봤을 때의 구역의 수를 공백으로 구분해 출력한다.

요구사항

  • 입력: N×NN \times N 크기의 그리드에 R(빨강), G(초록), B(파랑) 중 하나가 칠해져 있다.
  • 조건:
    • 상하좌우로 인접한 같은 색상은 하나의 구역으로 본다.
    • 적록색약인 사람은 RG를 구분하지 못해 같은 색으로 본다.
  • 출력: 적록색약이 아닌 사람이 봤을 때의 구역 수와, 적록색약인 사람이 봤을 때의 구역 수를 공백으로 구분하여 출력한다.

접근 방식

이 문제는 "연결된 덩어리(Connected Component)의 개수"를 세는 문제이므로 DFS(깊이 우선 탐색) 또는 BFS(너비 우선 탐색)를 사용해야 한다.

핵심 아이디어: 맵을 2개 만들기 (전처리)

적록색약 로직을 DFS 함수 안에서 if문으로 분기 처리하면 코드가 복잡해진다.
대신, 입력을 받을 때부터 일반인용 배열적록색약용 배열을 따로 만들면 로직을 통일할 수 있다.

  1. 배열 초기화

    • nor[][]: 입력받은 그대로 저장 (일반인용)
    • rgb[][]: 입력받을 때 만약 G라면 R로 바꿔서 저장 (적록색약용)
    • 이렇게 하면 rgb 배열에서는 RG가 모두 R로 통일되어 자연스럽게 한 덩어리로 인식된다.
  2. 구역 개수 세기 (countArea 함수)

    • 이중 for문을 돌면서 방문하지 않은 지점('c'가 아닌 곳)을 찾는다.
    • 방문하지 않은 곳을 발견하면 cnt를 1 증가시키고, DFS를 수행하여 연결된 모든 지점을 방문 처리한다.
    • 방문 처리는 별도의 boolean 배열 대신, 원본 배열의 값을 'c'로 변경하여 메모리를 절약한다.
  3. DFS 재사용

    • 위에서 맵을 2개로 분리해두었기 때문에, 하나의 dfs 함수와 countArea 함수를 두 배열에 똑같이 적용하기만 하면 된다.

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
    static int[] dx = {0, -1, 0, 1};
    static int[] dy = {-1, 0, 1, 0};

    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;
        st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        char[][] nor = new char[n][n];
        char[][] rgb = new char[n][n];

        for(int i=0; i<n; i++) {
            st = new StringTokenizer(br.readLine());
            String line = st.nextToken();
            for(int j=0; j<n; j++) {
                char color = line.charAt(j);
                nor[i][j] = color;
                rgb[i][j] = color == 'G' ? 'R' : color;
            }
        }

        int norCnt = countArea(n, nor);
        int rgbCnt = countArea(n, rgb);

        System.out.print(norCnt + " " + rgbCnt);
    }

    static int countArea(int n, char[][] map) {
        int cnt = 0;
        for(int i=0; i<n; i++) {
            for(int j=0; j<n; j++) {
                if(map[i][j] != 'c') {
                    dfs(i, j, n, map[i][j], map);
                    cnt++;
                }
            }
        }
        return cnt;
    }

    static void dfs(int x, int y, int n, char color, char[][] nor) {
        if(nor[x][y] != color) return;
        else {
            nor[x][y] = 'c';
            for(int i=0; i<4; i++) {
                int nx = x + dx[i];
                int ny = y + dy[i];
                if(nx < 0 || nx >= n || ny < 0 || ny >= n) continue;

                dfs(nx, ny, n, color, nor);
            }
        }
    }
}

느낀점

  1. 전처리의 힘

처음에는 DFS 함수 안에서 "적록색약 플래그"를 넘겨서 if (isColorBlind && (cur == 'R' || cur == 'G')) 처럼 복잡하게 짜야 하나 고민했다.

하지만 입력받을 때 G를 R로 바꿔버리는 간단한 전처리만으로 로직을 완전히 통일할 수 있었다. 데이터의 전처리가 알고리즘의 복잡도를 얼마나 낮춰주는지 깨달았다.

  1. 코드의 재사용성 (리팩토링)

처음에는 main 함수 안에 이중 for문을 두 번 복사해서 썼는데, 코드가 길고 지저분해 보였다.

탐색 로직을 countArea라는 별도 메서드로 분리하니 main 함수가 훨씬 깔끔해졌고, 실수할 확률도 줄어들었다. 코테에서도 중복되는 로직은 함수화하는 습관을 들여야겠다.

profile
힘들어도 조금만 더!

0개의 댓글