Algorithm
[Algo] 백준 25049 뮤직 플레이리스트 JAVA
조핑구
2024. 5. 7. 16:30
문제 바로가기 : https://www.acmicpc.net/problem/25049
메모리 : 86404KB
시간 : 496ms
언어 : Java 11
import java.beans.IntrospectionException;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine());
StringTokenizer st = new StringTokenizer(br.readLine());
long ans = 0;
long[] arr = new long[N];
for (int i = 0; i < N; i++) {
arr[i] = Long.parseLong(st.nextToken());
// 일단 전곡을 1회는 들어야하기 때문에 누적합 더해주기
ans += arr[i];
}
long[] Ldp = new long[N];
long[] maxLdp = new long[N];
maxLdp[0] = Ldp[0] = arr[0];
for (int i = 1; i < N; i++) {
// 전칸이 최대부분합으로 이어지는지, 새로 끊어가야하는지 판단
Ldp[i] = Math.max(Ldp[i - 1] + arr[i], arr[i]);
// 최대부분합을 구해주기
maxLdp[i] = Math.max(maxLdp[i - 1], Ldp[i]);
}
long[] Rdp = new long[N];
long[] maxRdp = new long[N];
maxRdp[N - 1] = Rdp[N - 1] = arr[N - 1];
for (int i = N - 2; i > 0; i--) {
Rdp[i] = Math.max(Rdp[i + 1] + arr[i], arr[i]);
maxRdp[i] = Math.max(maxRdp[i + 1], Rdp[i]);
}
long maxsum = 0;
// 전곡 다시듣기가 더 좋을지도?
maxsum = Math.max(0, maxLdp[N - 1]);
for (int i = 0; i < N - 2; i++) {
maxsum = Math.max(maxsum, maxLdp[i] + maxRdp[i + 1]);
}
ans += maxsum;
System.out.println(ans);
}
}
너무 어려운 문제였다ㅜ
- 부분합 중 최댓값을 구해야 한다.
- 노래는 두 번 돌아갈 수 있기 때문에 두 개 구해야 한다.
- 최댓값 두 개를 다른 부분에서 어떻게 구할까?
- 왼쪽에서 본 부분합최댓값과 오른쪽에서 본 부분합최댓값을 구하면 된다
- 왜 ???? 왼쪽과 오른쪽이 겹칠 수 없기 때문에 왼쪽 최댓값과 오른쪽 최댓값은 서로를 밀어내며 부분최대값 두개를 찾아야 한다.
- 양쪽의 최댓값을 더한 값 중에 또 최대를 찾아 답에 더한다.
왼쪽과 오른쪽의 부분합최댓값을 구하는 것에 카데인 알고리즘이 필요하다. 원래 최대 부분합을 구하려면 브루트포스로 완전탐색해야하는데, 이런 경우에 시간복잡도가 O(N^2)이기 때문에 O(N)으로 풀려면 카테인알고리즘을 사용해야한다.
Ldp[i] = Math.max(Ldp[i - 1] + arr[i], arr[i]);
부분합을 구해가면서 현재 칸(i)이 전부터 이어저오는 부분에 속하는것이 이익인지, i부터 새 부분합을 시작하는 것이 이익인지 결정해야 한다. dp[i]=Math.max(전칸까지의 부분합+현재칸 , 현재칸)