프로그래머스/자바 Lv2
[프로그래머스] Java Lv2. 코딩테스트 연습 > PCCP 기출문제 > 석유 시추 (BFS 예제)
행수쌤
2024. 12. 7. 15:22
728x90
반응형
문제설명
세로 길이가 n, 가로 길이가 m인 격자 모양의 땅 속에 여러 덩어리로 나누어 묻혀있는 석유가 있다.
수직으로 단 하나의 시추관을 뚫었을 때 가장 많은 석유를 뽑을 수 있는 시추관의 위치를 찾아라.
제한사항
- 1 <= land의 길이 = 땅의 세로길이 = n <= 500
- 1 <= land[i]의 길이 = 땅의 가로길이 = m <= 500
- land[i][j]는 i+1 행 j+1 열 땅의 정보를 나타냄
- land[i][j]는 0 이면 빈 땅을, 1 이면 석유가 있는 땅을 의미
접근방법
효율성 테스트를 하는 것으로 보아 시추관의 위치를 옮기며 전체 탐색을 하는 것이 아닌 오일의 위치에 대한 정보를 먼저 파악하는 것이 필요하다 -> BFS를 사용한다
land를 순차적으로 탐색하면서 석유(1)를 발견하면 해당 덩어리에 id를 부여하고 연결된 석유의 총 량을 파악한다.
시추관을 옮기면서 해당 위치에 존재하는 석유 id를 통해 석유 총 량 합계를 내서 최댓값을 구한다.
핵심로직
- 석유가 발견되는 순서대로 2부터의 숫자를 부여해서 석유 덩어리를 indexing한다.
- 0, 1은 이미 land에 부여되어 있는 숫자이다.
- 0, 1이 아닌 숫자는 이미 탐색이 완료된 석유이므로 탐색할 필요가 없다.
- 문제로 주어지는 매개변수를 변경하므로 좋은 방법은 아니다.
- BFS를 활용하여 최초 발견된 석유의 id를 설정한 석유로부터 연결된 석유를 파악해 석유 덩어리 총량을 구하고 전부 해당 id로 바꿔준다.
- 큐를 생성해 탐색할 석유 좌표를 관리한다.
- 해당 위치에 석유가 발견되면(1) 현재 탐색중인 석유 덩어리의 id를 부여하고, 석유 총량 리스트.get(id)에 +1 해준다.
- 석유가 발견된 위치의 상, 하, 좌, 우 좌표를 큐에 넣어 탐색을 계속한다.
- 그렇게 탐색된 석유의 양을 id에 해당하는 list에 저장한다.
- 시추관을 1부터 m까지 옮기며 해당 열에 있는 석유 id를 파악한 후 리스트에서 석유 총량의 합을 구한다.
입출력 예
- land = [[0, 0, 0, 1, 1, 1, 0, 0], [0, 0, 0, 0, 1, 1, 0, 0], [1, 1, 0, 0, 0, 1, 1, 0], [1, 1, 1, 0, 0, 0, 0, 0], [1, 1, 1, 0, 0, 0, 1, 1]]
- result = 9
코드 작성
import java.util.*;
class Solution {
// 각 석유 지역의 석유 양을 저장하는 리스트
private static List<Integer> oilQuantity = new ArrayList<>();
// 각 열(column)에서 연결된 석유 지역 ID를 저장하는 리스트
private static List<Set<Integer>> way = new ArrayList<>();
// 현재 석유 지역의 ID를 추적 (2부터 시작)
private static int oilIndex = 2;
private static int m = 0; // land 배열의 열 개수
private static int n = 0; // land 배열의 행 개수
public int solution(int[][] land) {
n = land.length; // 행의 개수
m = land[0].length; // 열의 개수
// 0번과 1번 인덱스는 사용하지 않음 (편의상 0으로 초기화)
oilQuantity.add(0);
oilQuantity.add(0);
// 각 열에 대해 초기화
for (int i = 0; i < m; i++) {
way.add(new HashSet<>());
}
// 전체 land 배열을 탐색
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// 석유 지역(값이 1)을 발견하면 BFS로 탐색
if (land[i][j] == 1) {
oilQuantity.add(0); // 새로운 석유 지역에 대해 초기 석유 양 추가
checkSameOil(land, i, j); // BFS 탐색
oilIndex++; // 다음 석유 지역 ID로 이동
}
}
}
// 최대 석유량 계산
int answer = 0;
for (int i = 0; i < m; i++) {
int sum = 0;
Set<Integer> wayOil = way.get(i); // 열(column)에 연결된 석유 지역의 ID 집합
for (int wayOilIndex : wayOil) {
sum += oilQuantity.get(wayOilIndex); // 해당 석유 지역들의 석유 양 합산
}
answer = Math.max(sum, answer); // 최대값 갱신
}
return answer; // 최대 석유량 반환
}
// BFS 탐색으로 동일한 석유 지역을 식별
private static void checkSameOil(int[][] land, int i, int j) {
Queue<int[]> queue = new LinkedList<>();
queue.add(new int[]{i, j}); // 시작 지점 큐에 추가
while (!queue.isEmpty()) {
int[] current = queue.poll(); // 현재 위치 꺼내기
int x = current[0]; // 현재 행
int y = current[1]; // 현재 열
// 범위를 벗어나거나 이미 방문한 경우 무시
if (x < 0 || x >= n || y < 0 || y >= m || land[x][y] != 1) {
continue;
}
// 현재 위치를 현재 석유 지역 ID로 마킹
land[x][y] = oilIndex;
// 해당 석유 지역의 석유 양 증가
oilQuantity.set(oilIndex, oilQuantity.get(oilIndex) + 1);
// 현재 열에 해당 석유 지역 ID 추가
way.get(y).add(oilIndex);
// 4방향(상, 하, 좌, 우)으로 BFS 확장
queue.add(new int[]{x - 1, y}); // 위쪽
queue.add(new int[]{x + 1, y}); // 아래쪽
queue.add(new int[]{x, y - 1}); // 왼쪽
queue.add(new int[]{x, y + 1}); // 오른쪽
}
}
}
728x90
반응형