백준 1912 C++ (연속합)

PS / / 2021. 2. 9. 23:38

LIS와 DP 응용문제였다.

LIS을 외우기만 하면 못 풀고, 이해하고 응용할 줄 아는 사람은 어렵지 않게 풀었을 것이다.

 

시간 제한 메모리 제한 정답률
1초 128MB 29.909%

문제

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

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

입력

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

출력

첫째 줄에 답을 출력한다.

 

핵심

  • LIS를 응용하는 문제이다.
  • dp[k] : k번째 숫자로 끝나는 부분합들 중 합이 가장 큰 값

코드

#include <iostream>
#include <algorithm>

using namespace std;

int n;
int arr[100010];
int dp[100010]; // dp[k] : k번째 수가 마지막인 부분합들 중 합이 가장 큰 값
int answer;



int main(int argc, char* argv[]) {
	ios_base :: sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    
    cin >> n;
    
    for(int i=1;i<=n;i++){
        cin >> arr[i];
    }
    
    dp[1] = arr[1]; // 초항은 당연히 첫번째 숫자 값이다.
    
    for(int i=2;i<=n;i++){
        if(dp[i-1] > 0){ // 이전에 더해놨던 게 양수라서 더하면 이득인 경우
            dp[i] = dp[i-1] + arr[i]; // 전 항에서 계산한 값(dp[i-1])을 더해줌.
        }
        else{ // 이전에 더해놨던게 음수라서 더하면 오히려 손해인 경우
            dp[i] = arr[i]; // 굳이 더하지 않음.
        }
    }
    
    answer = dp[1];
    for(int i=2;i<=n;i++){
        if(answer < dp[i]) answer = dp[i]; // dp[n]이 항상 최댓값은 아니기 때문에, dp의 값들 중 최댓값을 찾아야한다.
    }
    
    cout << answer << '\n';
	
	return 0;
}

해설

뭔가 LIS같은 문제라는 느낌이 들었을 것이다.

만약 LIS가 무엇인지 잘 모르겠다면, 백준 11503번을 공부해야한다. 내 블로그에도 풀이를 해놨으니 참고하면 좋다.

jaetsby.tistory.com/12

 

[BOJ / C++] 백준 11053번 가장 긴 증가하는 부분수열

LIS (Longest Increasing Subsequence) 기초 문제이다. 이 문제를 응용해서 낼 수 있는 문제가 많으므로 반드시 알고리즘을 알아두어야 한다. 시간 제한 메모리 제한 정답률 1초 256MB 36.752% 문제 수열 A가 주.

jaetsby.tistory.com

이제 모두가 알 것이라고 믿는다.

DP의 냄새도 맡았고, LIS의 냄새도 맡아버린 나머지 우리는 분명히 이전 항에 계산한 값을 다음 항에 계산할 때 써먹을 수 있겠구나라고 눈치챌 수 있다.

그럼 그걸 어떻게 하냐의 문제인데, LIS가 그 힌트를 우리에게 주고 있다.

LIS 문제를 풀 때 점화식을 보통 k번째 숫자(항)이 마지막인 부분수열들 중 최댓값 이라고 세울 것이다.

이번 문제도 똑같이 점화식을 k번째 숫자로 끝나는 부분합들 중 합이 가장 큰 값으로 세우면 금세 해결된다!😁😁

 

다만, 다른점이 하나 있다. 음수가 중간중간에 있다는 것이다.

우선 양수만 있다고 생각하면, 직전 항에서 계산해놓은 것에 현재 숫자를 더하기만 하면 연속합 값이 증가할 것이다.

dp[k] = dp[k-1] + arr[k] 해버리면 그만이다.

 

-5 6 -9 4

그렇지만, 만약 직전 항에서 계산한 값이 음수라면, 직전 항을 더하면 오히려 연속합 값이 감소한다.

따라서 그런 경우 dp[k] = arr[k] 를 해야 한다.

k=4인 상황에서 보면, 현재 숫자는 4인데, 앞을 보면 음수이다. 6 + (-9) 값은 더하면 오히려 -3이 되기 때문에,

앞의 값 잘라버리고 4부터 다시 시작하는 것이 좋다.

 

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기