INFO
1. 문제 이해
핵심 요구사항
- 여러 조각으로 나뉜 피자 A,B를 가지고 손님이 원하는 양을 판매
- 한 피자에서 2조각 이상을 파는 경우 연결해서 팔아야함.
- 목표: 판매하는 모든 방법의 가지 수 || 0
제한 조건
- 변수 범위
- 구매할 크기 ⇐ 2,000,000
- 3 ≤ m, n (조각 개수) ≤ 1000
- 1 ≤ (조각 크기) ≤ 1000
- 시간: 2초
- 메모리: 128MB
2. 사고의 흐름 (Thinking)
-
❌ 1차 접근: 완전 탐색 (Brute Force)
- 각 피자마다 가능한 조합을 구하고, 하나씩 다 확인해보는 방법.
- 그러나 조각의 개수 m, n이 각각 최대 1,000이라
- 피자 A의 조합 구하기 (= 경계선 2개 고르기) = 최악의 경우
- 피자 B의 조합 구하기 (= 경계선 2개 고르기) = 최악의 경우
- A에서 하나 B에서 하나씩 고르기 =
- 시간복잡도는 으로 100% 시간 초과
-
✅ 2차 시도: 구간합
- 문제에서 말하는 중요한 조건은 피자 사이즈이다
- 따라서,
- 조합에 포함되는 조각을 저장하는 것이 아니라, 해당 조합으로 나오는 피자 크기를 저장하기.
- 구해야하는 피자 사이즈가 S일 때, 피자 A에서 도출된 구간합 a에 대하여 피자 B에서 S-a인 값의 개수를 찾으면 됨.
-
✅ 3차 시도: 구간합 + Map 혹은 counting 배열
- 더하여 구간의 피자 크기 key 또는 index로이고 그 크기가 등장하는 가짓 수를 value로 하는 자료구조를 사용 → 탐색 속도 줄임.
3. 풀이 및 코드 (Solution)
import java.io.*;
public class BOJ2632_피자판매 {
static int order, M,N;
static int[] pizza_m;
static int[] pizza_n;
static int[] size_cnt_m = new int[2000001];
static int[] size_cnt_n = new int[2000001];
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
order = Integer.parseInt(br.readLine());
String[] temp = br.readLine().split(" ");
M = Integer.parseInt(temp[0]);
N = Integer.parseInt(temp[1]);
pizza_m = new int[M];
pizza_n = new int[N];
size_cnt_m[0] = 1;
size_cnt_n[0] = 1;
// 입력 받기 / 전체 합 구하기
int all_m=0, all_n=0;
for (int i = 0; i<M; i++) {
pizza_m[i] = Integer.parseInt(br.readLine());
all_m += pizza_m[i];
}
for (int i = 0; i<N; i++) {
pizza_n[i] = Integer.parseInt(br.readLine());
all_n += pizza_n[i];
}
// 가능한 조합의 합 구하기
if (all_m <= order) size_cnt_m[all_m] = 1;
if (all_n <= order) size_cnt_n[all_n] = 1;
prefix_sum(pizza_m, size_cnt_m, M);
prefix_sum(pizza_n, size_cnt_n, N);
long result = 0; // 가짓수 구하는 문제는 long이 안전하다고함...
for (int size = 0; size <= order; size++) {
if (size_cnt_n[size] > 0) {
result += (long) size_cnt_n[size]*size_cnt_m[order-size];
}
}
System.out.println(result);
}
static void prefix_sum(int[] piece, int[] sum_cnt, int len) {
// 조각이 2 ~ m-1(n-1) 개 일때 크기 저장
int sum;
for (int start = 0; start < len; start++) {
sum = 0;
for (int end = start; end < len+start-1; end++) {
sum += piece[end%len];
if (sum <= order) sum_cnt[sum]++;
else break;
}
}
}
}-
구간합은 누적합을 사용하여 구하였음.
-
시간 복잡도: → 부분합을 구하는 곳에서 이중 for문 사용
4. 회고 (Retrospective)
- 이번에도, 중요한 조건에 집중하는 습관이 필요했음.
- 누적합으로 구간합을 구하는 것도 알고 있었는데, 오래간만이라 이상하고 복잡하게 누적합을 구함ㅋㅋㅋㅋ → 문제 더 많이 풀자자