본문 바로가기

코딩테스트/codility

MaxDoubleSliceSum

문제

더보기

A non-empty array A consisting of N integers is given.

A triplet (X, Y, Z), such that 0 ≤ X < Y < Z < N, is called a double slice.

The sum of double slice (X, Y, Z) is the total of A[X + 1] + A[X + 2] + ... + A[Y − 1] + A[Y + 1] + A[Y + 2] + ... + A[Z − 1].

For example, array A such that:

    A[0] = 3

    A[1] = 2

    A[2] = 6

    A[3] = -1

    A[4] = 4

    A[5] = 5

    A[6] = -1

    A[7] = 2

contains the following example double slices:

  • double slice (0, 3, 6), sum is 2 + 6 + 4 + 5 = 17,
  • double slice (0, 3, 7), sum is 2 + 6 + 4 + 5 − 1 = 16,
  • double slice (3, 4, 5), sum is 0.

The goal is to find the maximal sum of any double slice.

Write a function:

class Solution { public int solution(int[] A); }

that, given a non-empty array A consisting of N integers, returns the maximal sum of any double slice.

For example, given:

    A[0] = 3

    A[1] = 2

    A[2] = 6

    A[3] = -1

    A[4] = 4

    A[5] = 5

    A[6] = -1

    A[7] = 2

the function should return 17, because no double slice of array A has a sum of greater than 17.

Write an efficient algorithm for the following assumptions:

  • N is an integer within the range [3..100,000];
  • each element of array A is an integer within the range [−10,000..10,000].

 

해설

정수 N개로 이루어진 배열 A와, x, y, z (0 <= x < y < z < N)가 주어졌을 때, A[x+1]부터 A[y-1]까지의 합과 A[y+1] ~ A[z-1] 까지의 합의 합이 최대가 되는 경우를 찾는 문제.

 

풀이

(x+1번째부터 y-1번째까지의 부분합) + (y+1번째부터 z-1번째까지의 부분합) 이 최대값이 되는 케이스를 찾아야하므로, 일단 두개의 부분합 배열이 필요하다.

// x+1 ~ y-1 부분합
int[] leftSum = new int[len];
initArr(leftSum);	// 배열 초기화(상세 코드 생략)

for (int i = 1; i < len; i++) {
	leftSum[i] = Math.max(leftSum[i-1] + A[i], 0);
}

// y+1 ~ z-1 부분합
int[] rightSum = new int[len];
initArr(rightSum);	// 배열 초기화(상세 코드 생략)
        
for (int i = len - 2; i > 0; i--) {
    rightSum[i] = Math.max(rightSum[i+1] + A[i], 0);
}

leftSum이 x+1~y-1번째까지의 부분합이되고,

rightSum이 y+1~z-1번째까지의 부분합이 됨.

 

두 배열에서 i는 y의 값을 나타낸다고 보면 됨.

 

부분합을 구할 때 Math.max의 역할이 중요함.

 

배열 A를 구성하는 정수들의 범위가 -10,000 ~ 10,000 이기때문에, 부분합이 음수 값이 되는 시점이 생길 수 있음

부분합이 음수다? 그러면 해당 구간의 부분합은 전체 부분합의 최대값을 떨어뜨리는 구간이 된다는 의미가 됨. 그럼 해당 구간을 버려야 한다는 의미로 0으로 초기화 해주는 것.

 

즉 leftSum을 기준으로 봤을 때, 0을 만나기전까지 x는 0이고, 0을 만나는 순간 x는 i가 된다고 보면 되지만, 구하고자하는 값이 x, y, z가 아니므로 크게 신경 안써도 됨

 

이제 y값(즉 배열의 i)를 옮겨가면서 두 배열의 합을 구하면 x+1~y-1, y+1~z-1 두 구간의 부분합이 구해지게 되며, 이를 이용해 최대값을 찾으면 된다.

 

for (int i = 1; i < len-1; i++) {
    int temp = leftSum[i-1] + rightSum[i+1];
    // System.out.printf("[%d] %d + %d = %d\n", i, leftSum[i-1], rightSum[i+1], temp);
    if (temp > sum) sum = temp;
}

 

 

 

소스

// you can also use imports, for example:
// import java.util.*;

// you can write to stdout for debugging purposes, e.g.
// System.out.println("this is a debug message");

class Solution {
    public int solution(int[] A) {
        // Implement your solution here
        int sum = 0;

        int len = A.length;

        if (len < 4) return sum;

        int[] leftSum = new int[len];
        initArr(leftSum);
        
        int[] rightSum = new int[len];
        initArr(rightSum);

        for (int i = 1; i < len; i++) {
            leftSum[i] = Math.max(leftSum[i-1] + A[i], 0);
        }

        // for (int left : leftSum) System.out.println(left);

        for (int i = len - 2; i > 0; i--) {
            rightSum[i] = Math.max(rightSum[i+1] + A[i], 0);
        }

        // for (int right : rightSum) System.out.println(right);

        for (int i = 1; i < len-1; i++) {
            int temp = leftSum[i-1] + rightSum[i+1];
            // System.out.printf("[%d] %d + %d = %d\n", i, leftSum[i-1], rightSum[i+1], temp);
            if (temp > sum) sum = temp;
        }


        return sum;
    }

    void initArr(int[] arr) {
        for (int i = 0; i < arr.length; i++) arr[i] = 0;
    }

}

 

'코딩테스트 > codility' 카테고리의 다른 글

PermCheck  (0) 2023.09.29
TapeEquilibrium  (0) 2023.09.18
OddOccurrencesInArray  (0) 2023.09.16
CyclicRotation  (0) 2023.09.11
BinaryGap  (0) 2023.09.07