새소식

Computer Science/코딩테스트 문제 풀이

⭐⭐⭐⭐⭐[백준][파이썬][DP] 1912번: 연속합

  • -

https://www.acmicpc.net/workbook/by/BaaaaaaaaaaarkingDog

 

 

https://www.acmicpc.net/problem/1912

 

 

문제

n개의 정수로 이루어진 임의의 수열이 주어진다. 우리는 이 중 연속된 몇 개의 수를 선택해서 구할 수 있는 합 중 가장 큰 합을 구하려고 한다. 단, 수는 한 개 이상 선택해야 한다.

예를 들어서 10, -4, 3, 1, 5, 6, -35, 12, 21, -1 이라는 수열이 주어졌다고 하자. 여기서 정답은 12+21인 33이 정답이 된다.

입력

첫째 줄에 정수 n(1 ≤ n ≤ 100,000)이 주어지고 둘째 줄에는 n개의 정수로 이루어진 수열이 주어진다. 수는 -1,000보다 크거나 같고, 1,000보다 작거나 같은 정수이다.

출력

첫째 줄에 답을 출력한다.

 

예제 입력 1 

10
10 -4 3 1 5 6 -35 12 21 -1

예제 출력 1 

33

예제 입력 2 

10
2 1 -4 3 4 -4 6 5 -5 1

예제 출력 2 

14

예제 입력 3 

5
-1 -2 -3 -4 -5

예제 출력 3 

-1
 

처음에 DP로 DP를 2차배열로 만들고 이중 for문을 사용하였으나, 메모리초과가 발생하였다. 

다음으로 DP를 1차배열로 만들고 이중 for문을 하나만 사용하였으나, 시간초과가 발생하였다.

 

첫 시도: 틀린답

def solution(nums):
    N = len(nums)
    largest_num = max(nums)
    
    # 1차원 dp 배열로 변환
    dp = [0] * N
    
    for i in range(N):
        dp[i] = nums[i]
        current_sum = nums[i]
        
        for j in range(i + 1, N):
            current_sum += nums[j]
            dp[i] = max(dp[i], current_sum)
            
            if dp[i] > largest_num:
                largest_num = dp[i]
    
    return largest_num
    

if __name__=="__main__":
    N = int(input())
    nums = list(map(int, input().split()))
    print(solution(nums))

 

현재 코드의 시간 복잡도는 O(N^2)입니다. 이는 모든 구간의 합을 일일이 계산하고 그 중에서 최대값을 찾는 방식이므로, N이 클 경우 성능 문제가 발생할 수 있습니다.

이 문제는 카데인 알고리즘(Kadane's Algorithm) 을 사용하여 시간 복잡도를 O(N)으로 줄일 수 있습니다. 카데인 알고리즘은 연속된 부분 배열 중 가장 큰 합을 구하는 효율적인 알고리즘입니다.

 

 

1차원배열로 1중 for문으로 구현이 필요하다. 

 

각 원소 i마다 그 앞의 부분합을 구할 필요는 없음.

즉, 부분합을 구해서 배열에 넣고 dp에 저장해봐야 메모리 초과, 시각 복잡도만 증가한다.

매 수간 '현재까지의 연속합'과 '현재 값' 간의 최대값을 정하자. 

'현재값'이 '현재까지의 연속합'보다 크면, 이제 '현재값'부터 시작이다.

 

정답

def solution(nums):
    # 현재까지의 최대 부분합과 전체 최대 부분합 초기화
    current_sum = nums[0]
    largest_sum = nums[0]
    
    for i in range(1, len(nums)):
        # 이전까지의 합(current_sum)과 현재 숫자(nums[i]) 중 더 큰 값 선택
        current_sum = max(nums[i], current_sum + nums[i])
        
        # 전체 최대 부분합 갱신
        largest_sum = max(largest_sum, current_sum)
    
    return largest_sum
    

if __name__=="__main__":
    N = int(input())
    nums = list(map(int, input().split()))
    print(solution(nums))

 

설명하자면 

10 -4 3 1 5 6 -35 12 21 -1

에서 

-35와 -35까지의 연속합 -14 중 더 큰 값은 -14이다. 가장 큰 연속합은 (10-4+3+1+5+6=21)이다.

12와 12까지의 연속합 -2 중 더 큰 값은 12이다. 여전히 가장 큰 연속합은 (10-4+3+1+5+6=21)이다.

 

즉 '이전까지의 연속합'+'현재 값'과 '현재 값'부터 시작 중

'현재 값'이 더 커지면 이제 '현재 값'부터 연속합을 살펴보면 된다. 

그러나 이전에 연속합 중 가장 최적의 값은 여전히 기억 중 이다.  (10-4+3+1+5+6=21)

 

그러다가 12+21로 연속합 값이 이전 가장 큰 연속합 값보다 커지면 

갱신된다. 

 

다른 사람 풀이 

def solution(nums):
    
    for i in range(1, len(nums)):
        nums[i] = max(nums[i], nums[i] + nums[i-1])
    
    return max(nums)
    

if __name__=="__main__":
    N = int(input())
    nums = list(map(int, input().split()))
    print(solution(nums))

 

와우 

Contents

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

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