코딩테스트/SWEA
[SWEA] 1952번 - 벌꿀채취 풀이 및 코드 분석 (Java)
현걸
2025. 2. 28. 16:09
1. 문제 분석
📌 문제 개요
N x N
크기의 벌통 배열이 있다.- 두 명의 일꾼이 가로로
M
개의 벌통을 선택하여 꿀을 채취해야 한다. - 두 일꾼의 선택한 벌통이 겹치면 안 된다.
- 각 일꾼은 C 이하의 꿀만 채취 가능하며, 수익은 꿀의 양의 제곱합이다.
- 최대 수익을 얻는 경우를 찾아야 한다.
🎯 요구사항
- 두 일꾼이 서로 겹치지 않는 위치에서 벌통을 선택해야 한다.
- 각 일꾼이 채취할 꿀의 양이
C
이하인 경우의 수익을 계산해야 한다. - 두 일꾼이 얻을 수 있는 최대 수익을 구해야 한다.
- 완전 탐색을 이용하여 가능한 모든 경우를 조사해야 한다.
2. 해결 방법
🔹 핵심 개념
- 모든 가능한 조합을 완전 탐색(Brute Force)
- 각 일꾼의 최대 수익을 구하는 방법: 부분집합 탐색(백트래킹)
🔑 해결 절차
- 모든 가능한 두 일꾼의 위치 조합을 탐색
first
일꾼이(r, c) ~ (r, c+M-1)
을 선택했을 때second
일꾼이first
와 겹치지 않도록 다른 위치를 선택
- 각 일꾼이 선택한 벌통에서 최대로 얻을 수 있는 수익을 계산
- 백트래킹을 활용한 부분집합 탐색
C
이하의 꿀을 채취하는 경우의 제곱합이 가장 큰 값을 찾기
- 두 일꾼의 최대 수익을 합산하여 최댓값을 갱신
max(total, ans1 + ans2)
3. 코드 구현
import java.io.*;
import java.util.*;
public class Solution {
static int N, M, C, maxProfit;
static int[][] honey;
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringBuilder sb = new StringBuilder();
StringTokenizer st;
int T = Integer.parseInt(br.readLine());
for (int t = 1; t <= T; t++) {
sb.append("#").append(t).append(" ");
maxProfit = 0;
st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
C = Integer.parseInt(st.nextToken());
honey = new int[N][N];
for (int r = 0; r < N; r++) {
st = new StringTokenizer(br.readLine());
for (int c = 0; c < N; c++) {
honey[r][c] = Integer.parseInt(st.nextToken());
}
}
findMaxProfit();
sb.append(maxProfit).append("\n");
}
System.out.println(sb);
}
// 두 일꾼이 선택할 수 있는 최대 수익 찾기
private static void findMaxProfit() {
for (int r1 = 0; r1 < N; r1++) {
for (int c1 = 0; c1 <= N - M; c1++) {
int profit1 = getMaxHoneyProfit(r1, c1);
for (int r2 = r1; r2 < N; r2++) {
for (int c2 = 0; c2 <= N - M; c2++) {
if (r1 == r2 && !(c2 >= c1 + M)) continue; // 겹치는 경우 제외
int profit2 = getMaxHoneyProfit(r2, c2);
maxProfit = Math.max(maxProfit, profit1 + profit2);
}
}
}
}
}
// 백트래킹을 이용한 최적의 수익 찾기
private static int getMaxHoneyProfit(int row, int col) {
int[] selected = new int[M];
for (int i = 0; i < M; i++) {
selected[i] = honey[row][col + i];
}
return getMaxSubsetProfit(selected, 0, 0, 0);
}
// 부분집합 탐색을 통한 최대 수익 계산
private static int getMaxSubsetProfit(int[] arr, int idx, int sum, int profit) {
if (sum > C) return 0; // 채취량 초과 시 무효
if (idx == arr.length) return profit; // 모든 벌통을 확인했을 때의 최댓값 반환
// 현재 벌통을 선택하는 경우와 선택하지 않는 경우 비교
return Math.max(
getMaxSubsetProfit(arr, idx + 1, sum + arr[idx], profit + arr[idx] * arr[idx]),
getMaxSubsetProfit(arr, idx + 1, sum, profit)
);
}
}
4. 코드 분석
🔹 주요 알고리즘
- 두 일꾼이 선택할 수 있는 모든 경우의 조합을 탐색
findMaxProfit()
에서 모든 가능한 위치에서 일꾼 1을 선택- 일꾼 1과 겹치지 않는 위치에서 일꾼 2를 선택
- 각 일꾼의 최대 수익을 계산
getMaxHoneyProfit()
을 통해 M개의 벌통에서 최대 수익 찾기getMaxSubsetProfit()
을 이용하여 부분집합 탐색(백트래킹)
- 부분집합 탐색을 활용한 최댓값 찾기
C
이하의 꿀을 채취하는 경우의 제곱합이 가장 큰 값을 찾음
🔹 시간 복잡도 분석
- 일꾼 1의 위치 선택:
O(N^2)
- 일꾼 2의 위치 선택:
O(N^2)
- 최대 수익 찾기 (부분집합 탐색):
O(2^M)
(최대O(32)
) - 총 시간 복잡도:
O(N^4 * 2^M) ≈ O(10^4 * 32) = O(3.2 * 10^5)
✔ N ≤ 10
, M ≤ 5
이므로 충분히 빠르게 동작 가능!
5. 개선할 점 및 대체 방법
✅ 개선할 점
- 메모이제이션을 추가하여 중복 계산 줄이기
getMaxSubsetProfit()
결과를 캐싱하면 연산 속도 향상 가능
- 비트마스킹을 활용한 부분집합 탐색
2^M
의 부분집합 탐색을 비트마스킹(1 << M
)으로 최적화 가능
- DFS + DP 활용 가능
dp[r][c]
를 사용하여 최대 수익을 저장하면 중복 계산을 줄일 수 있음
🔄 대체 방법
- Dynamic Programming(DP) 활용
- 부분집합 탐색을 DP로 변경하면 속도를 더 빠르게 최적화 가능
- Bitmask DP 적용 가능
M ≤ 5
이므로32
개의 부분집합을비트마스크
로 처리 가능