티스토리 뷰
백준 온라인 저지(BOJ) 1912번 연속합
https://www.acmicpc.net/problem/1912
1912번: 연속합
첫째 줄에 정수 n(1 ≤ n ≤ 100,000)이 주어지고 둘째 줄에는 n개의 정수로 이루어진 수열이 주어진다. 수는 -1,000보다 크거나 같고, 1,000보다 작거나 같은 정수이다.
www.acmicpc.net
* 사용언어 : C언어, C++
1. 문제
n개의 정수로 이루어진 임의의 수열에서
연속된 수를 합하여 구할 수 있는 값 중 가장 큰 값을 출력
예를 들어, 10, -4, 3, 1, 5, 6, -35, 12, 21, -1 이라는 수열이 주어졌다면 정답은 12+21 = 33 이 됨
2. 풀이
우선 단순하게 브루트 포스 방식으로 전체 탐색을 해보면
아래와 같이 이중 for문으로 구현하여 해결할 수 있습니다.
// 중략
ret = -1000;
sum = 0;
for (int i = 0; i < n; ++i) {
for (int j = i; j < n; ++j) {
sum += arr[j];
ret = max(ret, sum);
}
sum = 0;
}
printf("%d\n", ret);
이중 for문을 사용한 것에서 알 수 있듯이 시간 복잡도가 O(n^2) 이므로 좋은 방식이 아닙니다.
(그리고 이 코드로 완성하여 제출하면 시간초과로 오답입니다)
이 문제는 동적 계획법(Dynamic Programming)을 활용하여 O(n)의 시간 복잡도로 해결할 수 있습니다.
동적 계획법의 핵심은 직전에 계산한 결과값을 기억해두고 이를 활용하며 문제를 푸는 것입니다.
(동적 계획법의 더욱 적합한 명칭은 '기억하며 풀기' 입니다)
아래 예시로 해당 문제의 풀이법을 유도해보겠습니다.
3, 5, 6, -35, 12, 21, -1
1번까지의 연속합의 최대값은 [1번째 값(3)]인 3입니다. (여기서 3을 기억)
2번까지의 연속합의 최대값은 [직전에 기억한 최대값(3) + 2번째 값(5)]인 8입니다. (여기서 8을 기억)
3번까지의 연속합의 최대값은 [직전에 기억한 최대값(8) + 3번째 값(6)]인 14입니다. (여기서 14를 기억)
4번까지의 연속합의 최대값은 [직전에 기억한 최대값(14)]인 14입니다. (여기서 직전 최대(14) + 4번째 값(-35)인 -21을 기억)
> 다음 자리(5번) 계산 시, 1 ~ 4번을 더하면 손해라는 것을 알 수 있도록 -21을 기억합니다.
5번까지의 연속합의 최대값은 [5번 값(12)]인 12입니다. (여기서 12를 기억)
> -35를 포함하면 앞의 3, 5, 6 을 다 합쳐도 손해(-21)이므로 12를 기억하고 다시 시작합니다.
> 추가로 현재까지의 최대값 14를 전체의 최대값이라고 ret 변수에 따로 기록해둡니다.
6번까지의 연속합의 최대값은 [직전에 기억한 최대값(12) + 6번 값(21)]인 33 입니다. (여기서 33을 기억)
> 여기서 14가 12보다 크더라도 14를 사용하면 안됩니다.
> 14는 -35(4번)로 연속이 끊기기 전까지의 최대값이고, 5번째 값(12)부터 다시 시작했으므로 12에서 시작합니다.
> 추가로, 이번에 계산된 33이 기록해두었던 14보다 크므로 ret 를 33으로 바꿔줍니다.
7번까지의 연속합의 최대값은 [직전에 기억된 최대값(33) + 7번째 값(-1)]인 32입니다. (여기서 32를 기억)
따라서 이 수열의 최대 연속합은 33 (= 12 + 21) 이 됩니다.
위와 같이 문제를 풀고 일반화하여 정리하면 아래와 같습니다.
n번까지의 연속합의 최대값은 [n-1번까지의 최대값 + n번 값] or [n번 값] 둘 중 큰 값이다.
매번 계산되는 최대값은 dp[] 라는 배열에 기록하여 다음 번 연속합을 찾을 때 활용하고,
전체의 최대값은 ret 에 기록하여 계산된 값이 이를 초과하면 갱신합니다.
아래의 코드 참고하시어 index 가 하나하나 올라갈 때 어떻게 계산되는지 하나하나 생각해보시면
동적 계획법 소스 이해에 도움이 될 것 같습니다.
3. 코드
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
/*
1912_연속합
1892KB 16ms
*/
#include <cstdio>
const int LM = 100000;
int arr[LM], dp[LM], n, ret;
int max(int a, int b) {
if (a > b) return a;
return b;
}
int main() {
#ifdef _WIN32
freopen("input.txt", "r", stdin);
#endif // _WIN32
scanf("%d", &n);
for (int i = 0; i < n; ++i) scanf("%d ", &arr[i]);
ret = arr[0];
dp[0] = arr[0];
for (int i = 1; i < n; ++i) {
dp[i] = max(dp[i - 1] + arr[i], arr[i]);
ret = max(dp[i], ret);
}
printf("%d\n", ret);
return 0;
}
'개발자 > 문제풀이 (C언어)' 카테고리의 다른 글
[백준/BOJ] 1330번 두 수 비교하기 (C/C++) (0) | 2021.04.16 |
---|---|
[백준/BOJ] 2588번 곱셈 (C/C++) (0) | 2021.04.16 |
[백준/BOJ] 1806번 부분합 (C/C++) (0) | 2021.03.29 |
[백준/BOJ] 11729번 하노이 탑 이동 순서 (C/C++) (0) | 2021.03.21 |
[백준/BOJ] 10171번 고양이 (C/C++) (1) | 2020.12.27 |
- Total
- Today
- Yesterday
- 자료구조
- 유연함의힘
- JUNGOL
- 정올
- 정세현의통찰
- 안전운전특약
- 영화감상평
- 동탄에듀센터
- 최재천의공부
- 인간본성불패의법칙
- 삼성전자
- 나의첫죽음학수업
- 긴 자리 곱셈
- 독서감상평
- 시대예보
- 독서 감상평
- AdSendse
- 알고리즘
- 당신도느리게나이들수있습니다
- 관계가상처가되기전에
- 세상을 읽는 새로운 언어 빅데이터
- 문현공
- 자동차보험
- 긴 자리 덧셈 뺄셈
- 나는늘잘해야한다고생각한다
- 여가포인트
- 센터독서클럽
- 호암의마지막꿈
- 쿠프마케팅
- 동탄에듀센터2
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |