연속 펄스 부분 수열의 합
문제 풀이 시간 : 1시간
문제 요약
- 정수 수열
sequence가 주어짐 - 연속된 일부 구간(부분 수열)을 선택하고 같은 길이의 펄스 수열을 각 원소에 곱함
- 펄스 수열은 두 가지 형태 중 하나
- 곱셈 결과로 만들어진 연속 펄스 부분 수열의 합 중 가장 큰 값을 구하기
문제 풀이
처음 이 문제를 보고 부분 합을 구해야겠다는 생각을 하고 일단 1로 시작하는 펄스 수열과 -1로 시작하는 펄스 수열을 각각 곱해서 순차합을 구해보았다.
예시와 같이 [2, 3, -6, 1, 3, -1, 2, 4] 라는 수열이 있을 때, 1과 -1로 시작하는 펄스 수열을 곱하면 아래와 같다.
- 1로 시작하는 펄스 수열을 곱한 경우 :
[2, -3, -6, -1, 3, 1, 2, -4] - 1로 시작하는 펄스 수열을 곱한 경우 :
[-2, 3, 6, 1, -3, -1, -2, 4]
그리고 각 수열의 순차 합은 아래와 같다.
- 1로 시작하는 펄스 수열을 곱한 경우 :
[2, -1, -7, -8, -5, -4, -2, -6] - 1로 시작하는 펄스 수열을 곱한 경우 :
[-2, 1, 7, 8, 5, 4, 2, 6]
이 두가지 수열을 자세히 살펴보면 그냥 부호가 반대이고 동일한 수열인 것을 알 수 있다!
그래서 우리는 한가지 경우에 대해서만 계산하고 절댓값을 통해 최대 부분 수열의 합을 구할 수 있다.
그러면 이제 최대 부분 수열의 합을 구하는 것이 문제인데
이건 아주 쉽다.
우리는 이미 연속된 부분 수열의 합을 구해두었기 때문에 그런 부분 수열의 합 중에서 가장 큰 것과 작은 것을 빼주면 되는 것이다.
위의 1로 시작하는 펄스 수열에서 가장 큰 값은 2이고, 가장 작은 값은 -8 이므로 두가지 값을 빼주면 10이라는 최댓값이 나오게 된다.
초기 코드
import java.util.*;
class Solution {
public long solution(int[] sequence) {
long answer = 0;
int len = sequence.length;
long[] sum = new long[len+1];
sum[0] = sequence[0];
sum[len] = 0;
for(int i=1;i<len;i++){
if(i%2==0)
sum[i] = sum[i-1]+sequence[i];
else
sum[i] = sum[i-1]-sequence[i];
}
Arrays.sort(sum);
answer = sum[len] - sum[0];
return answer;
}
}처음엔 위와 같이 코드를 작성했다.
물론 이 코드도 동작하고 통과하는 코드이지만 생각해보니 이렇게 sum 배열을 계산하고, 정렬을 하는 불필요한 동작이 필요가 없었다!
최종 코드
class Solution {
public long solution(int[] sequence) {
long answer = 0;
int len = sequence.length;
long max = 0L;
long min = 0L;
long sum = 0L;
for(int i=0;i<len;i++){
if(i%2==0)
sum += sequence[i];
else
sum -= sequence[i];
max = Math.max(max, sum);
min = Math.min(min, sum);
}
return max - min;
}
}또 다른 풀이
이 문제는 당연히 다른 방식으로도 풀 수 있다.
부분 수열의 합을 구하는 방법에는 Kadane 알고리즘이 있다.
Kadane 알고리즘에 대한 설명은 블로그에 정리해두었다.
Kadane 알고리즘 코드
class Solution {
public long solution(int[] sequence) {
long answer = 0L;
long dp0 = 0L; // 1로 시작하는 펄스수열을 곱한 수열의 최대 부분합
long dp1 = 0L; // -1로 시작하는 펄스수열을 곱한 수열의 최대 부분합
for (int i = 0; i < sequence.length; i++) {
if ((i & 1) == 0) { // 짝수 인덱스
dp0 = Math.max(0L, dp0) + sequence[i];
dp1 = Math.max(0L, dp1) - sequence[i];
} else { // 홀수 인덱스
dp0 = Math.max(0L, dp0) - sequence[i];
dp1 = Math.max(0L, dp1) + sequence[i];
}
answer = Math.max(answer, Math.max(dp0, dp1));
}
return answer;
}
}함께 읽으면 좋은 글
택배상자
order 배열은 택배 기사님이 원하는 상자 적재 순서다 기존 컨테이너 벨트는 1번부터 순서대로만 꺼낼 수 있다 보조 벨트는 스택처럼 마지막에 넣은 것부터 꺼낼 수 있다 원하는 순서대로 최대한 실을 수 있는 상자 개수를 구하는 문제다 처음에는 기존 벨트를 실제 리스트로 만들어…
롤케이크 자르기
topping 배열은 롤케이크에 일렬로 올라간 토핑 번호다 한 지점을 잘라 두 조각으로 나눴을 때, 양쪽 토핑 종류 수가 같아야 공평하게 나눈 것이다 공평하게 자를 수 있는 방법의 수를 구하는 문제다 처음에는 자르는 위치마다 왼쪽과 오른쪽 배열을 나눠서 각각 서로 다른 토핑…
할인 행사
want, number 배열로 정현이가 원하는 제품과 수량을 표현한다 discount 배열은 XYZ 마트가 매일 할인하는 제품 목록이다 연속된 10일 동안 할인 제품 종류와 수량이 want, number와 정확히 일치해야 회원가입할 수 있다 그런 시작일이 총 몇 번 있는지…
숫자 변환하기
자연수 x를 y로 바꾸는 데 x+n, x2, x3 세 가지 연산을 쓸 수 있다 x를 y로 바꾸는 최소 연산 횟수를 구하는 문제다 만들 수 없으면 -1을 반환한다 처음에는 x에서 시작해서 세 가지 연산을 재귀적으로 다 시도해보고 y에 도달하는 경로 중 가장 짧은 걸 고르면…