출처 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; } }
'알고리즘 > algospot' 카테고리의 다른 글
[종만북][알고스팟] 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 |