새소식

반응형
Algotithms

[프로그래머스] 연속 펄스 부분 수열의 합

  • -
728x90
반응형

문제

  • 연속 펄스 부분 수열의 합 중 가장 큰 것 구하기
    • 펄스 수열 : 1 또는 -1 이 번갈아 나오는 수열
    • 연속 펄스 부분 수열 : 수열의 연속 부분 수열에 같은 길이의 펄스 수열의 각 원소 곱한 수열
  • 1 <= (수열 길이) <= 500,000
  • -100,000 <= (수열의 원소) <= 100,000

 

풀이 1

  • 각 연속 부분 수열에 곱할 수 있는 펄스 수열은 2개 (1로 시작할 때, -1로 시작할 때)
    • 연속 부분 수열에서 (홀수 번째 합 - 짝수 번째 합) 과 (짝수 번째 합 - 홀수 번째 합) 2개가 된다.
  • 홀수 번째의 누적합과 짝수 번째의 누적합을 구해두고, 모든 경우의 수 확인하기
  • 시간 복잡도 : $O(N^2)$ 
    • 통과되지 않을 거라는걸 알고 있었지만 역시나 시간초과
    • 전체 수열에서 연속 부분 수열은 길이 1 ~ N까지, 각 시작점에서 존재
      • N + (N-1) + ... + 1 => $N^2$ 개 이상의 경우의 수가 존재한다.

 

import sys

def solution(sequence):
    n = len(sequence)   # sequence 길이
    odd_sum = [0]       # 홀수 누적합
    for i in range(0, n, 2): 
        odd_sum.append(odd_sum[-1]+sequence[i])
        odd_sum.append(odd_sum[-1])
    eve_sum = [0, 0]    # 짝수 누적합
    for i in range(1, n, 2): 
        eve_sum.append(eve_sum[-1]+sequence[i])
        eve_sum.append(eve_sum[-1])
    
    answer = -sys.maxsize
    for s in range(1, n+1):
        # sequence[s:s+l] : (s+l-1)까지 합 - (s-1)까지의 합
        for l in range(1, n-s+2):
            odd = odd_sum[s+l-1] - odd_sum[s-1]
            eve = eve_sum[s+l-1] - eve_sum[s-1]
            answer = max(answer, odd-eve, eve-odd)
        
    return answer

 

 

풀이 2

  • "시간을 줄여야한다" 로 생각이 든건 DP 
    • dp[n][2][2] : n번째까지 수열 중 / n번째를 포함할 때 안할 때 / 부호가 + -
      • dp[n][0][0] : n번째까지 n번째를 포함하지 않을 때, 짝수 부호가 +
      • dp[n][0][1] : n번째까지 n번째를 포함하지 않을 때, 짝수 부호가 -
      • dp[n][1][0] : n번째까지 n번째를 포함할 때, 짝수 부호가 +
      • dp[n][1][0] : n번째까지 n번째를 포함할 때, 짝수 부호가 -
  • 시간 복잡도 : O(N)
import sys

def solution(sequence):
    n = len(sequence)   # sequence 길이

    # dp 테이블
    dp = [[[-sys.maxsize]*2 for _ in range(2)] for _ in range(n)]
    # dp 테이블 초기화 - 자기 자신일 때를 최댓값으로 설정
    for i in range(n):
        flag = 1 if i % 2 else -1       # flag : 홀수면 1 짝수면 -1

        dp[i][1][0] = sequence[i] * (-flag) # 짝수 부호가 +
        dp[i][1][1] = sequence[i] * flag    # 홀수 부호가 +

    for i in range(1, n):
        # i 번째를 포함하지 않을 때 최댓값
        dp[i][0][0] = max(dp[i-1][0][0], dp[i-1][1][0])
        dp[i][0][1] = max(dp[i-1][0][1], dp[i-1][1][1])     
        # i 번째를 포함할 때 최댓값
        flag = 1 if i % 2 else -1       # flag : 홀수면 1 짝수면 -1
        dp[i][1][0] = max(dp[i][1][0], dp[i-1][1][0] + sequence[i] * (-flag))
        dp[i][1][1] = max(dp[i][1][1], dp[i-1][1][1] + sequence[i] * flag)
    
    return max(dp[n-1][0][0], dp[n-1][0][1], dp[n-1][1][0], dp[n-1][1][1])

 

 

풀이 3

  • 더 간단한 DP 점화식
    •  dp[0][n] : n번째까지 중 현재 부호가 + 일 때, 최댓값 
    • dp[1][n] :  n번째까지 중 현재 부호가 - 일 때, 최댓값
  • 새로운 구간합을 시작하는 경우 vs (이전까지 구간합 + 현재 값) 을 비교한다.
def solution(sequence):
    N = len(sequence)
    # dp 테이블
    dp = [[0] * N for _ in range(2)]
    # dp 테이블 초기화 
    dp[0][0] = sequence[0]          # 짝수일 때 +
    dp[1][0] = sequence[0] * -1     # 짝수일 때 -

    for i in range(1, N):
        # i번째로 새로운 구간 시작 vs (이전까지 구간합 + 현재 값)
        dp[0][i] = max(sequence[i], dp[1][i-1] + sequence[i])
        dp[1][i] = max(-sequence[i], dp[0][i-1] - sequence[i])

    return max(max(dp[0]), max(dp[1]))

 

 

풀이 4

  • 펄스 적용된 sequence에서 구간[a-b]의 합이 최대가 정답
  • 최대 누적합[b] - 최소 누적합[a] 인 구간[a,b]가 최댓값을 갖는 연속 부분 수열이 된다.
def solution(sequence):
    answer = 0
    prefixS = [0]
    for i in range(len(sequence)):
        # [1, -1, .., ] 펄스 적용
        pulse = 1 if i % 2 == 0 else -1 
        # 구간합 구하기
        prefixS.append(prefixS[-1] + pulse * sequence[i])
    
    # 최대 누적합[b] - 최소 누적합[a] 인 구간[a,b]가 최댓값을 갖는 연속 부분 수열
    return abs(max(prefixS) - min(prefixS))

 

 

 

References

  • 프로그래머스 Lv3 연속 펄스 부분 수열의 합
반응형

'Algotithms' 카테고리의 다른 글

[백준] 17144 미세먼지 안녕!  (0) 2023.04.27
[백준] 16937 두 스티커  (0) 2023.04.27
[백준] 1238 파티  (0) 2023.04.26
[백준] 16956 늑대와 양  (0) 2023.04.26
[프로그래머스] 베스트 앨범  (0) 2023.04.26
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.