Computer >> 컴퓨터 >  >> 프로그래밍 >> Python

파이썬으로 배열을 세 개의 연속된 부분 배열로 나누는 방법의 수 구하기

문제 소개

정수로 이루어진 배열 nums가 주어졌을 때, 이 배열을 "좋은(good)" 방식으로 분할하는 서로 다른 경우의 수를 구하는 프로그램을 만들어 보겠습니다. 답이 매우 커질 수 있으므로 최종 결과는 10^9 + 7로 나눈 나머지를 반환해야 합니다.

여기서 "좋은 분할"이란 다음 조건들을 동시에 만족하는 분할을 의미합니다.

  • 배열을 왼쪽에서 오른쪽 순서대로 비어 있지 않은 세 개의 연속된(contiguous) 부분 배열로 나눈다.
  • 왼쪽 부분의 원소 합 ≤ 가운데 부분의 원소 합
  • 가운데 부분의 원소 합 ≤ 오른쪽 부분의 원소 합

예시

입력이 nums = [2,3,3,3,7,1]일 때 출력은 3입니다. 가능한 분할 방식은 다음 세 가지입니다.

  • [2], [3], [3,3,7,1]
  • [2], [3,3], [3,7,1]
  • [2,3], [3,3], [7,1]

풀이 접근: 누적 합 + 두 포인터

모든 분할 지점을 완전 탐색하면 O(n²) 이상의 시간이 걸릴 수 있습니다. 하지만 누적 합(prefix sum) 배열과 두 포인터(two pointers) 기법을 함께 사용하면 O(n) 시간 복잡도로 문제를 해결할 수 있습니다. 배열의 원소가 음수가 아니라면 누적 합은 항상 단조 증가하므로, 경계를 가리키는 두 포인터 r과 rr는 한 번 오른쪽으로 이동한 뒤 되돌아올 필요가 없기 때문입니다.

알고리즘의 전체 흐름은 다음과 같습니다.

  • n := nums의 길이
  • m := 10^9 + 7
  • ss := 크기가 (n+1)인 배열을 만들고 0으로 초기화(누적 합 저장용)
  • nums의 각 인덱스 i와 값 val에 대해 → ss[i] := ss[i-1] + val
  • r := 0, rr := 0, ans := 0으로 초기화
  • l을 1부터 n-2까지 반복:
    • r := max(r, l+1)
    • r < n-1이고 ss[r] − ss[l] < ss[l]인 동안 r을 1씩 증가
    • rr := max(rr, r)
    • rr < n-1이고 ss[n] − ss[rr+1] ≥ ss[rr+1] − ss[l]인 동안 rr을 1씩 증가
    • ss[l] > ss[r] − ss[l]이면 반복문 종료(break)
    • ss[r] − ss[l] > ss[n] − ss[r]이면 다음 반복으로 건너뜀(continue)
    • ans := (ans + rr − r + 1) mod m
  • ans 반환

여기서 포인터 r은 "가운데 합이 왼쪽 합보다 작아지지 않는" 첫 번째 위치를 찾고, rr은 "오른쪽 합이 가운데 합보다 작아지지 않는" 마지막 위치를 찾습니다. 따라서 l이 고정된 상태에서 유효한 가운데 경계의 개수는 rr − r + 1이 됩니다.

파이썬 구현 예제

아래 코드를 통해 실제 구현 과정을 확인해 보겠습니다.

def solve(nums):
    n, m = len(nums), 10**9+7
    ss = [0] * (1+n)
    for i, val in enumerate(nums, 1):
        ss[i] = ss[i-1] + val

    r = rr = ans = 0
    for l in range(1, n-1):
        r = max(r, l+1)
        while r < n-1 and ss[r]-ss[l] < ss[l]:
            r += 1
        rr = max(rr, r)
        while rr < n-1 and ss[n]-ss[rr+1] >= ss[rr+1]-ss[l]:
            rr += 1
        if ss[l] > ss[r]-ss[l]:
            break
        if ss[r]-ss[l] > ss[n]-ss[r]:
            continue
        ans = (ans+rr-r+1) % m
    return ans

nums = [2,3,3,3,7,1]
print(solve(nums))

입력 및 출력 확인

입력

[1,7,3,6,5]

출력

3

배열 [1,7,3,6,5]의 경우 총 세 가지 방법으로 "좋은 분할"을 만들 수 있으며, 프로그램이 올바르게 3을 출력하는 것을 확인할 수 있습니다.