본문 바로가기

Algorithm

최대합 부분배열

문제 : 길이가 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