문제 : 길이가 n 인 정수의 배열 A[0..n-1]가 있다. A[a] + A[a+1] + … + A[b]의 값 을 최대화하는 구간 (a,b)를 찾는 방법을 Divide-and-Conquer 전략을 이용 하여 설계하고 분석하라. 예를 들어, 배열 A 가 아래와 같이 주어졌을 경우 (n = 10), 31 -41 59 26 -53 58 97 -93 -23 84 답은 a = 2, b = 6 인 경우의 59+26-53+58+97=187 가 된다.
이 문제에 대해 여러가지 방법이 있는데 가장 쉬운 방법부터 소개하겠다.
1. 부분합 수열 : O(n^2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
int MaxLoof(int *arr, int n)
{
int i,j,tmp,res;
res=0;
for(i=0;i<=n;i++)
{
tmp=0;
for(j=i;j<=n;j++)
{
tmp+=arr[j];
if(tmp>res)
res=tmp;
}
}
return res;
}
|
무식하게 푸는 방법으로 배열의 모든 경우의 합을 구하는 것이다.
하지만 이 방법은 Divide-and-Conquer 전략을 이용하지도 않는다.
좀 더 빠르고 효율적으로 계산하기 위한 방법은 분할 정복이 있다.
2. 분할정복 : O(nlogn)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
#include <stdio.h>
int Maximum(int *arr, int a, int b)
{
if(a==b)
return arr[a];
int mid=(a+b)/2;
int left=Maximum(arr,a,mid);
int right=Maximum(arr,mid+1,b);
int i,tmp,left_part,right_part;
tmp=0;
left_part=-999999;
for(i=mid;i>=a;i--)
{
tmp+=arr[i];
left_part=Max(left_part, tmp);
}
tmp=0;
right_part=-999999;
for(i=mid+1;i<=b;i++)
{
tmp+=arr[i];
right_part=Max(right_part,tmp);
}
return Max2(left,right,left_part+right_part);
}
|
분할 정복은 정답을 구할 구간이 다음 세 가지 중에 하나라는 것에 중점을 둔다.
1. [a, mid], 배열의 왼쪽 부분에 있을 경우
2. [mid+1, b], 배열의 오른쪽 부분에 있을 경우
3. 양쪽 구간 모두에 걸쳐져 있는 경우
계속 반으로 분할하고 비교해서 최대가 되는 값만을 선택하면 최종적으로 최대값을 갖는 구간의 합을 얻게 된다.
'Algorithm' 카테고리의 다른 글
네트워크 플로우(Network Flow) 및 이분 매칭(Bipartite Matching) (0) | 2022.03.07 |
---|---|
Merge Sort (0) | 2022.02.20 |
ArrayList를 활용한 모든 경우의 수 탐색 (0) | 2021.08.08 |
멱집합 (0) | 2021.07.25 |
소수 판별 알고리즘 (2) | 2020.04.18 |