참고: 프로그래밍 대회에서 배우는 알고리즘 문제 해결 전략 (구종만 저)
이 글의 코드는 원저의 C++ 코드를 파이썬으로 옮긴 코드입니다.
1차원 배열에서 연속된 부분 구간 중 그 합이 최대인 구간을 찾는 문제는 여러 알고리즘으로 해결할 수 있다. 시간 복잡도가 서로 다른 여러 알고리즘을 구현하고 각 알고리즘의 수행 시간이 어떻게 다른지 확인해본다.
테스트 배열로는 $A = \begin{bmatrix} -7, 4, -3, 6, 3, -8, 3, 4 \end{bmatrix}$ 을 사용하며, 해답은 $\begin{bmatrix}4, -3, 6, 3 \end{bmatrix}$ 일 때 10이다.
1. 실행 시간 $O(N^3)$
1 |
|
2. 실행 시간 $O(N^2)$
1 |
|
3. 실행 시간 $O(N\log{N})$
분할 정복과 탐욕 알고리즘을 사용하면 실행 시간을 $O(N\log{N})$으로 줄일 수 있다. 1) 입력받은 배열을 반으로 잘라 왼쪽 배열과 오른쪽 배열로 나눈다. 2) 최대 합 부분 구간은 두 배열 중 하나에 속해 있을 수도 있고, 두 배열 사이에 걸쳐 있을 수도 있다. 3) 이 때 각 경우의 답을 재귀와 탐욕법을 이용해 계산하면 분할 정복 알고리즘이 된다.
1 |
|
4. 실행 시간 $O(N)$
동적 계획법을 사용하면 선형 시간에 문제를 풀 수 있다. 배열 $A[i]$를 오른쪽 끝으로 갖는 구간의 최대 합을 리턴하는 함수 $max At(i)$를 정의해보자. 이 때, $A[i]$ 에서 끝나는 최대 합 부분 구간은 항상 $A[i]$ 하나만으로 구성되어 있거나, $A[i-1] $을 오른쪽 끝으로 갖는 최대 합 부분 구간의 오른쪽에 $A[i]$ 를 붙인 형태로 구성되어 있음을 증명할 수 있다. 따라서 $maxAt()$ 를 다음과 같은 점화식으로 표현할 수 있다.
\[maxAt(i) = \max(0, maxAt(i-1)) + A[i]\]1 |
|
1초 안에 처리할 수 있는 최대 입력 크기는 각 알고리즘에 따라 다음과 같이 변한다.
- $O(N^3)$: 크기 2560인 입력까지를 1초 안에 풀 수 있다. $2560^3$은 대략 160억이다.
- $O(N^2)$: 크기 40960인 입력까지를 1초 안에 풀 수 있다. $40960^2$은 대략 16억이다.
- $O(N\log{N})$: 크기가 대략 2천만인 입력까지를 1초 안에 풀 수 있다. 이 때 $N\log{N}$은 대략 5억이다.
- $O(N)$: 크기가 대략 1억 6천만인 입력까지 1초 안에 풀 수 있다.