| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- Ubuntu서버
- 스프링부트 배포
- spring security
- 자바의 정석
- 오블완
- 백트래킹
- java
- 분할정복 알고리즘
- 분할정복
- 프로그래머스 2개 이하로 다른 비트
- EC2배포
- 프로그래머스
- 리눅스
- 혼공얄코
- DFS
- 프로그래머스 둘만의 암호
- BFS
- 자바의정석
- 둘만의 암호 자바
- 서버초기설정
- systemd
- hackerrank
- SQL Mapper
- Comparable
- 알고리즘
- LocalDate
- 둘만의 암호
- 티스토리챌린지
- AWS배포
- spring security 설정
- Today
- Total
쉽게 쉽게
[프로그래머스] 무인도 여행 — BFS 본문
- 연결 요소별 합계 — 격자에서 상하좌우로 이어진 육지(섬)를 BFS로 묶고, 섬마다 식량의 합을 구하는 문제입니다.
- 거리 BFS vs 집계 BFS — 미로 탈출처럼 거리를 구할 땐 큐에
{r, c, day}를 싣지만, 이 문제는 탐색 밖의 변수 하나에 누적하면 충분합니다.
1 문제 소개
문자열 배열로 주어지는 지도에서 X는 바다, 숫자는 그 칸에서 얻을 수 있는 식량입니다. 상하좌우로 연결된 숫자 칸들이 하나의 무인도를 이룰 때, 각 섬에서 얻을 수 있는 식량의 총합을 오름차순으로 반환해야 합니다. 섬이 하나도 없으면 {-1}을 반환합니다.
- 문자열 배열을
char[][]맵으로 먼저 전부 변환합니다. - 이중 루프를 돌며 아직 방문하지 않은 육지 칸을 만나면 BFS를 시작합니다.
- BFS가 퍼지는 동안 방문하는 칸의 식량을 지역 변수
total에 누적하고, 탐색이 끝나면 그 값이 곧 섬 하나의 식량 합입니다. - 섬별 합계를 오름차순 정렬해
int[]로 변환합니다. 리스트가 비어 있으면{-1}을 반환합니다.
한 문장으로 요약하면 "연결 요소(Connected Component)를 찾고, 각 요소의 값을 합산하라"는 문제입니다. 얼마 전에 풀었던 미로 탈출과 똑같이 격자 BFS인데, 막상 풀어보니 큐에 실어야 하는 정보가 완전히 달랐습니다. 이번 포스트의 핵심은 바로 그 차이입니다.
2 접근: 미방문 육지마다 BFS 한 번씩
전체 구조는 격자 연결 요소 문제의 전형적인 패턴입니다. 바깥에서 이중 루프로 모든 칸을 훑다가, 바다가 아니고 아직 방문하지 않은 칸을 만나면 거기서 BFS를 한 번 돌립니다. BFS 한 번이 섬 하나를 통째로 방문 처리하므로, 같은 섬의 다른 칸에서는 두 번 다시 BFS가 시작되지 않습니다.
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (map[i][j] != 'X' && !visited[i][j]) {
answerList.add(bfs(i, j)); // BFS 1회 = 섬 1개의 식량 합
}
}
}
여기서 visited를 클래스 필드로 두는 게 포인트입니다. BFS 호출이 끝나도 방문 기록이 유지돼야 "이미 처리한 섬"을 건너뛸 수 있기 때문입니다. 각 칸은 정확히 한 번씩만 방문되므로 전체 시간 복잡도는 O(N×M)입니다.
3 핵심: 그 값은 누구의 것인가?
처음에는 미로 탈출을 풀던 습관대로 큐에 {행, 열, 값} 형태로 값을 실어 나르려고 했습니다.
그런데 이 문제에서는 그럴 필요가 전혀 없었습니다.
두 방식의 차이를 정리해보니, 기준은 "그 값이 칸마다 다른 답을 가지는가?"라는 질문 하나였습니다.
밖의 변수 = 탐색 전체의 집계
무인도의 식량 합계는 특정 칸의 속성이 아닙니다.
"이 칸의 합계"라는 개념 자체가 없고, 탐색이 끝났을 때 하나만 나오면 되는 값입니다.
이런 값은 노드마다 들고 다닐 이유가 없으니 BFS 바깥의 지역 변수에 누적하면 끝입니다.
// [거리 BFS] 값이 칸마다 다름 → 큐에 실어 부모→자식으로 전파
deque.add(new int[]{nr, nc, current[2] + 1});
// [집계 BFS] 값이 탐색 전체에 하나 → 밖의 변수에 누적
total += map[nr][nc] - '0';
deque.add(new int[]{nr, nc});
| 구분 | 거리 BFS (큐에 싣기) | 집계 BFS (밖에서 누적) |
|---|---|---|
| 값의 주인 | 노드 각각 | 탐색(컴포넌트) 전체 |
| 값이 정해지는 방식 | 부모 값에서 파생 (부모 + 1) |
방문한 칸들의 합·개수 |
| 답을 읽는 시점 | 목표 칸을 발견할 때 | while 루프 종료 후 |
| 대표 문제 | 미로 탈출 최단거리 | 무인도 여행 섬 크기·합 |
5 전체 코드
import java.util.*;
class Solution {
static final int[] dx = {-1, 1, 0, 0};
static final int[] dy = {0, 0, 1, -1};
int row, col;
char[][] map;
boolean[][] visited;
public int[] solution(String[] maps) {
row = maps.length;
col = maps[0].length();
map = new char[row][col];
visited = new boolean[row][col];
// 1단계: 맵 완성
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
map[i][j] = maps[i].charAt(j);
}
}
// 2단계: 미방문 육지마다 BFS
List<Integer> answerList = new ArrayList<>();
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (map[i][j] != 'X' && !visited[i][j]) {
answerList.add(bfs(i, j));
}
}
}
if (answerList.isEmpty()) {
return new int[]{-1};
}
return answerList.stream()
.sorted()
.mapToInt(Integer::intValue)
.toArray();
}
public int bfs(int startRow, int startCol) {
ArrayDeque<int[]> deque = new ArrayDeque<>();
deque.add(new int[]{startRow, startCol});
visited[startRow][startCol] = true;
int total = map[startRow][startCol] - '0'; // 시작 칸 식량 포함!
while (!deque.isEmpty()) {
int[] current = deque.poll();
int curRow = current[0];
int curCol = current[1];
for (int i = 0; i < 4; i++) {
int nextRow = curRow + dx[i];
int nextCol = curCol + dy[i];
if (nextRow >= 0 && nextCol >= 0 && nextRow < row && nextCol < col
&& map[nextRow][nextCol] != 'X' && !visited[nextRow][nextCol]) {
visited[nextRow][nextCol] = true;
total += map[nextRow][nextCol] - '0'; // 방문 처리와 같은 시점에 합산
deque.add(new int[]{nextRow, nextCol});
}
}
}
return total;
}
}
각 칸을 정확히 한 번씩만 방문하므로 시간 복잡도는 O(N×M)으로 최적입니다. 이 문제는 방문 순서가 결과에 영향을 주지 않아서 DFS로 풀어도 무방하지만, 격자 탐색은 BFS 패턴으로 통일해두는 편이 스택 오버플로 걱정도 없고 익숙해서 BFS를 선택했습니다.
6 마무리
"BFS는 이미 할 줄 안다"고 생각했는데, 미로 탈출의 틀을 그대로 가져왔다가 큐에 실을 필요 없는 값을 실어 나르며 헤맸습니다. 같은 격자 BFS라도 문제가 "몇 번 만에 도달하는가"를 묻는지 "연결된 덩어리의 합이 얼마인가"를 묻는지에 따라 상태 설계가 완전히 달라진다는 걸 몸으로 배운 문제였습니다.
정리하면 이번 문제에서 챙겨갈 것은 세 가지입니다.
- 상태 설계 질문 — 큐에 싣기 전에 "이 값이 칸마다 다른가?"를 먼저 묻는다.
- 초기화 → 탐색 순서 — 자료구조를 다 만들고 나서 그 위에서 탐색을 시작한다.
- 방문·합산 시점 통일 — 큐에 넣는 순간 방문 처리와 합산을 함께 하고, 시작 칸은 루프 밖에서 미리 더한다.
| 잘못된 내용이 있다면 지적부탁드립니다. 방문해주셔서 감사합니다. |

'알고리즘 > BFS (너비 우선 탐색)' 카테고리의 다른 글
| [알고리즘] BFS(너비 우선 탐색)란 무엇인가? (백준 2606, 2178) (0) | 2026.07.18 |
|---|---|
| [프로그래머스] 거리두기 확인하기(Java) — BFS (0) | 2026.07.18 |
| [프로그래머스] 미로 탈출 (Java) - BFS (0) | 2026.07.08 |
| [프로그래머스] 전력망을 둘로 나누기 (Java) — BFS (0) | 2026.07.03 |
| [프로그래머스] 리코쳇 로봇(Java) - BFS (1) | 2026.01.27 |
