출처 4.6 수행 시간 어림짐작하기 - 실제 적용해보기 (종만북)
코드설명
널리 알려진 문제입니다.
주어진 배열이 존재할떄, 최대 합을 갖는 부분 구간을 구합니다.
입력 예시 1
8
-7 4 -3 6 3 -8 3 4
결과 예시 1
[4, -3, 6, 3] 이 최대 부분 구간 합입니다.
10
코드
첫번쨰 방법입니다. 무작정 2중 for문을 사용한다면 너무 쉽게 구할 수 있으므로 넘어갑니다. 2중 for문으로 구하면 O(N^2)으로 구할 수 있습니다. 일종의 누적합 혹은 투포인터라고 생각하시면 됩니다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
public static int C, N, M;
public static int answer = Integer.MAX_VALUE;
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
int[] A = new int[N];
st = new StringTokenizer(br.readLine());
for(int i=0;i<N;i++) {
A[i] = Integer.parseInt(st.nextToken());
}
System.out.println(partialSum(A));
}
public static int partialSum(int[] A) {
int ret = Integer.MIN_VALUE;
for(int i=0;i<A.length;i++) {
int partialSum = 0;
for(int j = i; j<A.length;j++) {
partialSum += A[j];
ret = Math.max(ret, partialSum);
}
}
return ret;
}
}
이 방법보다 더 안좋은 방법도 존재합니다. 만약 첫번쨰 방법과 다르게 모든 구간을 새로 연산한다면, O(N^3)이 됩니다.
두번쨰 방법입니다. 분할정복 기법을 활용합니다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
public static int C, N, M;
public static int answer = Integer.MAX_VALUE;
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
int[] A = new int[N];
st = new StringTokenizer(br.readLine());
for(int i=0;i<N;i++) {
A[i] = Integer.parseInt(st.nextToken());
}
System.out.println(getMaxSumByDivideAndConquer(A, 0, A.length - 1));
}
//최대 연속부분 구간 합 문제를 푸는 분할 정복 알고리즘
// A[lo..hi]의 연속된 부분 구간의 최대합을 구합니다. 시간복잡도는 O(nlogn)
public static int getMaxSumByDivideAndConquer(int[] A, int lo, int hi) {
//기저사례 : 구간의 길이가 1 일 경우
if(lo == hi) return 1;
//배열을 A[lo..mid], A[mid + 1 ..hi] 의 두 조각으로 나눈다.
int mid = (lo + hi) / 2;
//두 부분에 모두 걸쳐있는 최대합 구간을 찾는다.
//이 구간은 A[i..mid]와 A[mid+1..j] 형태를 갖는 구간의 합으로 이루어진다.
//A[i..mid] 형태를 갖는 최대 구간을 찾는다.
int left = Integer.MIN_VALUE, right = Integer.MIN_VALUE, sum = 0;
for(int i=mid; i>=lo; i--) {
sum += A[i];
left = Math.max(left, sum);
}
//A[mid+1..j] 형태를 갖는 최대 구간을 찾는다.
sum = 0;
for(int j=mid+1; j<=hi; j++) {
sum += A[j];
right = Math.max(right, sum);
}
//최대 구간이 두 조각 중 하나에만 속해있는 경우의 답을 재귀호출로 찾는다.
int single = Math.max(getMaxSumByDivideAndConquer(A, lo, mid), getMaxSumByDivideAndConquer(A, mid+1, hi));
//두 경우 중 최대치를 반환한다.
return Math.max(left + right, single);
}
}
세번째는, 점화식을 이용합니다.
getMaxSum(i) = Math.max(getMaxSum(i - 1), 0) + A[i];
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
public static int C, N, M;
public static int answer = Integer.MAX_VALUE;
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
int[] A = new int[N];
st = new StringTokenizer(br.readLine());
for(int i=0;i<N;i++) {
A[i] = Integer.parseInt(st.nextToken());
}
System.out.println(getMaxSum(A));
}
public static int getMaxSum(int[] A) {
int tempSum = 0;
int ret = 0;
for(int i=0;i<N;i++) {
tempSum = Math.max(tempSum, 0) + A[i];
ret = Math.max(ret, tempSum);
}
return ret;
}
}
'알고리즘 > 알고리즘 문제해결 전략' 카테고리의 다른 글
[종만북][알고스팟] PICNIC(소풍) - 브루트포스(BruteForce) JAVA (0) | 2024.05.24 |
---|---|
[종만북][알고스팟] BOGGLE(보글 게임) - 브루트포스(BruteForce) + 동적계획법(Dynamic Programming) JAVA (0) | 2024.05.24 |
[종만북] 선택정렬(Selection Sort)과 삽입정렬(Insertion Sort) - JAVA (0) | 2024.05.15 |
[종만북] 가장 간단한 형태의 소인수 분해 알고리즘 - 완전탐색(BruteForce) JAVA (0) | 2024.05.15 |
[종만북] 알러지가 심한 친구들 음식 메뉴 정하기 - 완전탐색(BruteForce) + 재귀호출(Recursion) JAVA (0) | 2024.05.15 |