일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 29 | 30 |
- java
- CSS
- 백준
- js
- react
- Algorithm
- 알고리즘 문제
- 장고
- 알고리즘 풀이
- Git
- django ORM
- 알고리즘 연습
- PYTHON
- 파이썬 알고리즘
- es6
- form
- 파이썬
- DRF
- AWS
- django rest framework
- 알고리즘
- web
- MAC
- django widget
- javascript
- HTML
- Baekjoon
- API
- c++
- Django
- Today
- Total
수학과의 좌충우돌 프로그래밍
[Algorithm] 연속 구간의 최대 합 구하기 본문
규정 상의 이유로 출처와 문제는 밝힐 수 없지만 코딩테스트를 보다가 연속 구간의 최대 합을 구하는 문제를 접하게 되었습니다.
문제를 푸는 방법이 굉장히 다양하고 각 풀이 방법의 복잡도가 전부 다르기 때문에 이에 대해서 정리해보고자 합니다.
연속 구간의 합
우선 main 함수입니다.
main 함수에서는 원소의 개수를 입력받고, 원소의 개수만큼 값들을 입력받아 배열에 저장합니다.
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int main() {
int N;
vector<int> arr;
cin >> N;
for (int i=0;i<N;i++){
int temp;
cin >> temp;
arr.push_back(temp);
}
int result = brute_force(arr);
cout << result << "\n";
return 0;
}
Brute Force
첫 번째 방법은 Brute Force
입니다.
모든 구간의 합을 전부 계산하여 비교하는 방식이죠.
시간복잡도는 O(n^3)
입니다.
int brute_force(vector<int> arr){
int size = arr.size();
int result = INT_MIN;
for (int i=0;i<size;i++){
for (int j=0;j<size;j++){
int sum = 0;
for (int k=i;k<=j;k++){
sum += arr[k];
}
result = max(result, sum);
}
}
return result;
}
Brute Force 2
마찬가지로 Brute Force
이지만 불필요한 연산을 많이 개선하였습니다.
예를 들어 위의 경우, 3번째 원소부터 7번째 원소까지의 합과 3~8까지의 합을 각각 다시 계산하게 되지만 개선된 방법에서는 3~7까지 구한 값에서 8번째 값을 더해줌으로서 복잡도를 낮추었습니다.
시간복잡도는 O(n^2)
입니다.
int brute_force2(vector<int> arr){
int size = arr.size();
int result = INT_MIN;
for (int i=0;i<size;i++){
int sum = 0;
for (int j=i;j<size;j++){
sum += arr[j];
result = max(result, sum);
}
}
return result;
}
Divide And Conquer
이번에 사용할 방법은 Divide And Conquer
입니다.
전체의 부분합을 구하기 위해서 절반씩 나눠 작은 문제로 쪼갠 후 계산을 하게 됩니다.
하지만 여기서 문제점은 부분합의 최대값이 작은 문제에 걸쳐있는 상황입니다.
예를 들어 {-3, 0, -1, 3, 5, -2, -4}
의 부분합의 최대값은 3+5로 8이지만 절반으로 나눠 각각 계산하면 이를 구할 수 없습니다.
따라서 작은 문제로 나누기 전, 걸치는 상황에 대해 계산을 우선적으로 해줍니다.
시간복잡도는 O(nlogn)
입니다.
int divide_and_conquer(vector<int> arr, int start, int end){
if (start == end) return arr[start];
int result = INT_MIN;
int mid = (start + end) / 2;
// 부분합의 최대값이 mid를 포함하여 걸치는 상황
int left = INT_MIN;
int right = INT_MIN;
int left_sum = 0;
int right_sum = 0;
for (int i=mid;i>=start;i--){
left_sum += arr[i];
left = max(left, left_sum);
}
for (int i=mid+1;i<=end;i++){
right_sum += arr[i];
right = max(right, right_sum);
}
// 부분합이 mid를 기준으로 한 쪽에 생기는 상황
result = max(divide_and_conquer(arr, start, mid), divide_and_conquer(arr, mid+1, end));
result = max(result, left+right);
return result;
}
Dynamic Programming
마지막으로 Dynamic Programming
입니다.
dp 점화식을 정의해봅시다.
dp[i]
를 arr[i]를 오른쪽 끝으로 하는 구간의 최대 합
으로 정의해봅시다.
이 때 dp[i+1]
와 dp[i]
의 관계를 생각해보면 arr[i+1]
가 추가되었을 때 arr[i+1]
를 그 전까지의 최대 dp[i]
와 더한 값 혹은 독자적으로 arr[i+1]
값 중 하나가 dp[i+1]
이 됩니다.
그 후 dp[i]
를 전부 순회하면서 최대값을 찾으면 됩니다.
복잡도는 O(n)
입니다.
int dynamic_programming(vector<int> arr){
int result = INT_MIN;
int size = arr.size();
vector<int> dp(size);
for (int i=0;i<size;i++){
if (dp[i-1] + arr[i] > arr[i]) dp[i] = dp[i-1] + arr[i];
else dp[i] = arr[i];
}
for (int i=0;i<size;i++){
result = max(result, dp[i]);
}
return result;
}
'알고리즘 > 이론' 카테고리의 다른 글
[Algorithm] Counting Sort, 계수 정렬 (0) | 2020.10.23 |
---|---|
[Algorithm] 위상정렬 (0) | 2020.08.27 |
[Algorithm] 불안정정렬 sort, 안정정렬 stable_sort (0) | 2020.03.25 |
[Algorithm] 플로이드 와샬(Floyd-Warshall) 알고리즘 (3) | 2019.11.10 |
[Algorithm] 벨만포드(Bellman-Ford) 알고리즘 (0) | 2019.11.07 |