[BOJ 16198] 에너지 모으기 (Java) - 경로 압축(next[])을 접목한 백트래킹 풀이
백준 16198번(에너지 모으기) 문제에서 구슬 개수 N <= 10 제약을 이용한 DFS 백트래킹을 구현하면서, ArrayList 삭제 대신 Union-Find 방식의 좌우 인덱스 점프(next[])를 활용한 풀이를 정리합니다.
문제 정보
- 문제 링크: 백준 16198번 - 에너지 모으기
- 난이도: Silver I
- 시간 / 메모리 제한:
1초/512 MB
핵심 조건 요약
N(3 ≤ N ≤ 10)개의 에너지 구슬이 일렬로 놓여 있으며, 각 구슬의 무게는W_i(1 ≤ W_i ≤ 1,000)입니다.- 첫 번째와 마지막 구슬을 제외한
x번째 구슬을 골라 제거하고W_{x-1} × W_{x+1}의 에너지를 모읍니다. - 구슬이 2개 남을 때까지 반복할 때 모을 수 있는 에너지 합의 최댓값을 구합니다.
접근 아이디어
N ≤ 10이므로 선택 가능한 구슬 순서의 최대 경우의 수는 (10 - 2)! = 8! = 40,320가지뿐입니다.
따라서 모든 제거 순서를 시도하는 **백트래킹(DFS)**으로 넉넉히 해결할 수 있습니다.
List.remove() 대신 visit[] + 경로 점프(next[]) 활용
보통 ArrayList에서 원소를 삭제(remove)했다가 백트래킹 시 add(i, val)로 복구하는 방식을 많이 씁니다.
여기서는 배열에서 실제 원소를 빼지 않고도 양옆에 살아있는 구슬을 빠르게 찾기 위해 Union-Find의 경로 압축과 유사한 findLeft / findRight + next[] 점프 배열을 적용했습니다.
visit[i] == true이면 해당 구슬은 이미 제거된 상태입니다.i번째 구슬을 선택할 때findLeft(i - 1)(또는findLeft(i))와findRight(i)로 가장 가까운 미방문 좌/우 구슬의 인덱스를 찾아energy[left] * energy[right]를 더합니다.- 재귀가 끝나고 백트래킹할 때는
visit[i] = false;와 함께next[i] = i;로 인덱스를 원래대로 복구합니다.
Java 풀이 코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import static java.util.Arrays.stream;
public class Main {
static int maxEnergy = Integer.MIN_VALUE;
static int n;
static int[] energy;
static boolean[] visit;
static int[] next;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
n = Integer.parseInt(br.readLine());
energy = stream(br.readLine().split(" "))
.mapToInt(Integer::parseInt)
.toArray();
visit = new boolean[n];
next = new int[n];
for (int i = 0; i < n; i++) {
next[i] = i;
}
getEnergy(0, 0);
System.out.println(maxEnergy);
}
public static void getEnergy(int depth, int totalEnergy) {
// 양 끝(0번, n-1번) 구슬 2개만 남으면 종료
if (depth == n - 2) {
maxEnergy = Math.max(totalEnergy, maxEnergy);
return;
}
for (int i = 1; i < n - 1; i++) {
if (!visit[i]) {
visit[i] = true;
int right = findRight(i);
int left = findLeft(i);
getEnergy(depth + 1, totalEnergy + energy[right] * energy[left]);
visit[i] = false;
next[i] = i;
}
}
}
public static int findRight(int x) {
if (!visit[x]) {
return x;
}
return next[x] = findRight(x + 1);
}
public static int findLeft(int x) {
if (!visit[x]) {
return x;
}
return next[x] = findLeft(x - 1);
}
}
주의 및 참고: 위
findLeft/findRight에서next[x]를 캐싱해 두었다면, 백트래킹 시 다른 중간 노드들의next값이 함께 갱신되어 있을 수 있지만if (!visit[x]) return x;가 먼저 검사되고 양방향 모두visit[x] == true일 때만x + 1/x - 1로 이동하므로 항상 정상 동작합니다. (사실next[]캐싱 없이while (visit[left]) left--;로 구현해도N <= 10이라 동일하게 빠르고 더 단순합니다.)
채점 결과

복잡도 분석
- 시간 복잡도:
O((N - 2)! × N)—N = 10일 때8! × 10 ≈ 4 × 10^5연산 수준 - 공간 복잡도:
O(N)—energy,visit,next배열 및 최대 깊이N - 2재귀 스택
[BOJ 1406] 에디터 (Java) - 0.3초 시간 초과 해결을 위한 두 개의 스택(Two Stacks)
NEXT LOG →[BOJ 12100] 2048 (Easy) (Java) - 큐(Queue) 병합 시뮬레이션과 2차원 배열 원복