# 1.4. MSS

In 

최대 부분 수열의 합 Maximum Subsequence Sum

# Maximum Subsequence Sum (MSS) Problem

  • Ref. [M. Weiss, Data Structure and Algorithm Analysis in C (2nd ed.), Pearson, 1997. 2.4.3]

    • Given N (possiblly negative) A_0, A_1, ..., A_{N-1} \in \mathbb{Z}
    • find the maximum value of \sum_{k=i}^{j} {A_k } for 0 \leq i \leq j \leq N-1
    • for convenience, the max subseuqence sum is 0 if all the integers 're <0
  • Example

    • (-2, 11, -4, 13, -5, -2). → MSS = 20

      스크린샷 2021-06-13 오후 4 00 54
  • Maximum Subarray Problem

  • Maximum Positive Sum Subarray Problem

  • Max. Sum Subsequence versus Max. Subsequence Sum

# Alg of Maximum Subsequence Sum

  • 길이 $n$인 정수의 수열 a_0, a_1, ..., a_{n-1} 이 입력으로 주어져 있다.

  • 여기서 부분 수열 [i, j]라는 것은 a_i, a_{i+1}, a_{i+2}..., , a_{j} 를 말한다.

  • 본 문제는 주어진 수열의 부분 수열의 합,즉 \sum_{i \leq k \leq j} {a_k} 의 최대값을 구하는 문제이다. (이때 주어진 수열의 정수가 모두 음수이면 최대 부분 수열의 합은 0 이라고 간주한다)

    • 예를 들어 다음과 같은 수열이 주어졌을 때, + 31, −41, +59, +26, −53, +58, +97, −93, −23, +84 최대 부분 수열은 $[2,6]$이며 수열의 합은 187 이 된다.
  • 이 문제는 최대 부분 수열의 합을 구하는 것이지만, 앞으로 소개할 알고리즘을 조금만 수정하면 최대 부분 수열도 쉽게 구할 수 있다.

    Algorithm1 : 모든 경우의 수 찾기 - O(N^3) Algorithm2 : Sum구할 때 중복 조금 피하기 - O(N^2) Algorithm3 : Divide n Conquer - O(N log N) Algorithm4 : Dynamic Programming - O(N)

    • image

# MSS Algorithm 1

  • Strategy
    • Enumerate all possibilities one at a time.

    • No efficiency is considered, resulting in a lot of unnecessary computation!

      Maxsum = 0
      for (i=0; i < n; i++){
          for (j=i; j < n; j++){
              Thissum = sum(A[i:j])
            	Maxsum = max(Thissum, Maxsum)
          }
      }
    • 모든 경우의 수를 하나하나 모두 따져보는 방법.

      int MaxSubsequenceSum(const int A[], int N){
      int ThisSum, MaxSum, i, j, k;
        //i = 리스트 왼쪽 끝 인덱스, j = 리스트 오른쪽 끝 인덱스, ThisSum = 고려 대상 부분 리스트 합, MaxSum = 문제 최종결론
      MaxSum = 0;
      for (i = 0; i < N; i++)
          for (j = i; j < N; j++)
          {
              ThisSum = 0;
              for (k = i; k <= j; k++)
                  ThisSum += A[k];
              if (ThisSum > MaxSum)
                  MaxSum = ThisSum;
          }
      return MaxSum;
      }
      
    • Is this for-loop OK for you?

    • Time Complexity : O(N^3)

      • 𝑖와 관련된 반복문은 𝑛n번, 𝑗와 관련된 반복문은 최대 𝑛번, Thissum을 구할 때 최대 𝑛개의 요소를 계산해야 하기에
      • \sum_{i=0}^{N-1} \sum_{j=i}^{N-1} \sum_{k=i}^{j} 1 = \frac{N^3 + 3N^2 + 2N}{6}
      • \sum_{j=i}^{N-1}\ (j-i+1) = \frac{(N-i+1)(N-i)}{2}
      • \sum_{k=i}^{j} 1 = j-i+1

# MSS Algorithm 2

  • Strategy
    • Get rid of the inefficiency in the innermost for-loop. Algorithm 1보다 중복을 줄이는 방법

    • Maxsum = 0
      for (i=0; i < n; i++){
            for (j=i; j < n; j++){
                Thissum = sum(A[i:j])
              	Maxsum = max(Thissum, Maxsum)
              }
          }
      • Notice that \sum_{k=i}^{j } {A_k} = A_j + \sum_{k=i}^{j-1} {A_k}

      • int MaxSubsequenceSum(const int A[], int N)
        {
          int ThisSum, MaxSum, i, j;
          MaxSum = 0;
        for (i = 0; i < N; i++){
          ThisSum = 0;
          for (j = i; j < N; j++){
              ThisSum += A[j];
            if (ThisSum > MaxSum)
              MaxSum = ThisSum;
          }
        }return MaxSum;

      }

    • time complexity : O(N^2)

# MSS Algorithm 3

  • Strategy

    • Use the Divide-and-Conquer strategy.

      • 원 문제를 작은 문제로 나눠 풀고, 그 결과를 합쳐 문제를 해결하는 알고리즘
    • The maximum subsequence sum can be in one of three places.

      int MaxSubSum(const int A[], int Left, int Right){
      int MaxLeftSum, MaxRightSum;
      int MaxLeftBorderSum, MaxRightBorderSum;
      int LeftBorderSum, RightBorderSum;
      int Center, i;
        //종료조건
      if (Left == Right){
          if (A[Left] > 0)
      				return A[Left];
          else
              return 0;
      }
      //divide n conquer 
      Center = (Left + Right) / 2;
      //왼쪽, 오른쪽, 중간
      MaxLeftSum = MaxSubSum(A, Left, Center);
      MaxRightSum = MaxSubSum(A, Center + 1, Right);
      MaxLeftBorderSum = 0;
      LeftBorderSum = 0;
      
      //1. left ending 끝으로 하는 mss
      for (i = Center; i >= Left; i--){
          LeftBorderSum += A[i];
          if (LeftBorderSum > MaxLeftBorderSum)
              MaxLeftBorderSum = LeftBorderSum;
      }
      MaxRightBorderSum = 0;
      RightBorderSum = 0;
      //2. right ending 시작으로 하는 mss
      for (i = Center; i <= Right; i++){
          RightBorderSum += A[i];
          if (RightBorderSum > MaxRightBorderSum)
              MaxRightBorderSum = RightBorderSum;
      }
      return Max3(MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBorderSum);
      }
      int MaxSubsequenceSum(const int A[], int N) { 
      	return MaxSubSum(A, 0, N - 1); 
      }
      
  • cost : T(n) = 2T(n/2) + cn, T(1) = d

  • why O(N log N) ?

    • T(n) = 2T(\frac n 2) + cn, T(1) = d $ = 2 [ 2T(\frac n {2^2}) + c \frac n 2 ] + cn$ = 2^2 T [ \frac n {2^2}] + 2cn = 2^3 T [ \frac n {2^3}] + 3cn =... = 2^i T [ \frac n {2^i}] + icn = 2^{log_2 n} T(1) + log_2 n \cdot cn =nT(1) + log_2 n \cdot cn = O(n) + O(n log_2 n) = O(n log_2 n)

# MSS Algorithm 4; Kadane’s algorithm

  • Strategy
    • Use the Dynamic Programming strategy.

    • subsequence sum<0인 경우, 논리적으로 최대값이 될 수 없음에 착안한 전략

    • 만약에 sum이 음수라도 무방하고 1개 이상의 원소로 구 성된 Subsequence (subarray)를 구하는 문제라면?

      int MaxSubsequenceSum(const int A[], int N){
          int ThisSum, MaxSum, i;
      
          ThisSum = 0; MaxSum = 0;
          for(i = 0; i < N; i++){
              ThisSum += A[i];
              if(ThisSum > MaxSum)
                  MaxSum = ThisSum;
              else if(ThisSum < 0)
                  ThisSum = 0;
          }
          return MaxSum;
      }
    • Time Complexity : O(n)

      • for i, iteration n times, and O(1) for 1 calculation
    • C Implementation

      • Maximum sum rectangle in a 2D matrix (DP-27) by GeeksforGeeks
int kadane(int *arr, int *start, int *finish, int n)
{
    int sum = 0, maxSum = INT_MIN;

    *finish = -1;
    int local_start = 0;
    for (int i = 0; i < n; ++i)
    {
        sum += arr[i];
        if (sum < 0)
        {
            sum = 0;
            local_start = i + 1;
        }
        else if (sum > maxSum)
        {
            maxSum = sum;
            *start = local_start;
            *finish = i;
        }
    }
    if (*finish != -1)
        return maxSum; 
    // at least one non-negative number.
    
    // When all numbers in the array are negative
    maxSum = arr[0];
    *start = *finish = 0;
    for (int i = 1; i < n; i++)
    {
        if (arr[i] > maxSum)
        {
            maxSum = arr[i];
            *start = *finish = i;
        }
    }
    // Empty subsequence를 허용하면 0을 리턴 (원래 문제)
    // Empty subsequence를 허용하지 않으면 음수 중 가장 큰 원소를 리턴
    return maxSum;
}
# So, why do we bother with the time complexity?