정수로 이루어진 배열 A가 주어졌을 때, 어떤 값 sum[i]를 기준으로 배열 전체를 합이 sum[i]와 동일한 연속된 부분 배열들로 나눌 수 있다면, 그러한 조건을 만족하는 모든 합계 값을 찾아야 합니다. 만약 어떤 합으로도 배열을 균등하게 나눌 수 없다면 -1을 반환합니다.
문제 예시
예를 들어 입력이 A = [2, 4, 2, 2, 2, 4, 2, 6]이라면 출력은 [6, 8, 12]가 됩니다. 이 배열은 합이 6, 8, 12인 부분 배열들로 각각 나눌 수 있기 때문입니다.
- 합이 6일 때: {2, 4}, {2, 2, 2}, {4, 2}, {6}
- 합이 8일 때: {2, 4, 2}, {2, 2, 4}, {2, 6}
- 합이 12일 때: {2, 4, 2, 2, 2}, {4, 2, 6}
해결 접근 방법
이 문제의 핵심 아이디어는 두 가지입니다. 첫째, 누적합(prefix sum)을 미리 계산해 두면 임의의 지점까지의 구간 합을 빠르게 확인할 수 있습니다. 둘째, 배열 전체의 합 S를 k개의 동일한 합을 가진 구간으로 나누려면 반드시 S의 약수여야 한다는 점입니다. 따라서 S의 약수만 후보로 삼아 검증하면 효율적으로 답을 구할 수 있습니다.
알고리즘 단계
- n := 배열 a의 크기
- table := 크기가 n이고 0으로 채워진 배열 (누적합 저장용)
- table[0] := a[0]
- i를 1부터 n-1까지 순회하며 table[i] := a[i] + table[i-1] 계산
- S := table[n-1], 즉 배열 전체의 합
- my_map := 새로운 맵을 만들고, 모든 누적합 table[i]를 키로 등록
- answer := 결과를 저장할 새로운 집합(set)
- i를 1부터 √S의 정수 부분 + 1까지 순회하며:
- S mod i == 0이라면 i는 S의 약수이므로 두 가지 경우를 검사
- part_1 := i 인 경우, part_1부터 S까지 part_1씩 증가하며 해당 값이 my_map에 모두 존재하는지 확인 → 존재하고 part_1 ≠ S라면 answer에 추가
- part_2 := S // i 인 경우, 마찬가지로 part_2부터 S까지 part_2씩 증가하며 존재 여부 확인 → 존재하고 part_2 ≠ S라면 answer에 추가
- S mod i == 0이라면 i는 S의 약수이므로 두 가지 경우를 검사
- answer의 크기가 0이면 -1 반환
- 그렇지 않으면 answer 반환
여기서 my_map에 누적합을 미리 등록해 두는 이유는, 합이 part_1 또는 part_2인 구간들이 배열을 정확히 잘라낼 수 있으려면 그 배수 지점(part_1, 2×part_1, 3×part_1, ...)마다 누적합이 존재해야 하기 때문입니다.
구현 예제
아래 코드를 통해 더 자세히 이해해 보겠습니다.
from math import sqrt
def find_sum(a) :
n = len(a)
table = [0] * n
table[0] = a[0]
for i in range(1, n) :
table[i] = a[i] + table[i - 1]
S = table[n - 1]
my_map = {}
for i in range(n) :
my_map[table[i]] = 1
answer = set()
for i in range(1, int(sqrt(S)) + 1) :
if (S % i == 0) :
is_present = True;
part_1 = i
part_2 = S // i
for j in range(part_1 , S + 1, part_1) :
if j not in my_map :
is_present = False
break
if (is_present and part_1 != S) :
answer.add(part_1)
is_present = True
for j in range(S // i , S + 1 , S // i) :
if j not in my_map:
is_present = False;
break
if (is_present and part_2 != S) :
answer.add(part_2)
if(len(answer) == 0) :
return -1
return answer
a = [2, 4, 2, 2, 2, 4, 2, 6]
print(find_sum(a))입력
[2, 4, 2, 2, 2, 4, 2, 6]
출력
{8, 12, 6}복잡도 분석
약수 탐색은 O(√S), 각 약수에 대한 검증은 O(S/약수)이므로 전체 시간 복잡도는 대략 O(S log S) 수준이며, 누적합 배열과 맵에 O(n)의 공간이 사용됩니다. 완전 탐색으로 모든 분할 지점을 조사하는 것보다 훨씬 효율적인 접근 방식입니다.