목차
문제 : boj30646
필요 알고리즘
- 누적 합(prefix sum), 해시를 사용한 집합과 맵
- 누적합과 해시맵을 사용해 풀 수 있다. 물론 언제나 다른 방법들도 있다.
※ 제 코드에서 왜 main 함수에 로직을 직접 작성하지 않았는지, 왜 Scanner를 쓰지 않고 BufferedReader를 사용했는지 등에 대해서는 '자바로 백준 풀 때의 팁 및 주의점' 글을 참고해주세요. 백준을 자바로 풀어보려고 시작하시는 분이나, 백준에서 자바로 풀 때의 팁을 원하시는 분들도 보시는걸 추천드립니다.
풀이
우선 누적합을 모른다면 '누적 합(prefix sum), 2차원 누적합(prefix sum of matrix) with java' 글을 보자.
이 문제를 풀기위한 로직을 생각해보면 다음을 알 수 있어야 한다.
a값에 대해 의미있는건 각 a값이 최초로 나왔던 인덱스와 마지막에 나왔던 인덱스값이다. 예를들어 입력이 다음과 같다고 해보자.
5
1 1 1 1 1
위의 입력에서 중요한건 a값이 '1'인 경우에 대해 최초로 나왔던건 첫번째이고, 마지막에 나왔던건 5번째라는 점이다. 왜냐하면 이 문제에서 a값은 항상 양의 정수이므로, 최대한 넓은 범위를 합치는게 언제나 이득이다. 즉 i=1, j=5 이외에 i=1, j=3인 경우 등은 아예 생각할 이유가 없다.
그럼 위의 정보를 어떻게든 모두 알고 있다고 하자. 그럼 이제 효율적으로 특정 인덱스 사이에 존재하는 값들의 합을 알 수 있어야 한다. 이 때 가장 효율적인게 누적합 알고리즘이다.
정리하자면
1. N개의 a값에 대해, 서로 다른 a값들의 최초 인덱스와 마지막 인덱스를 기록한다. 이 때 a의 범위는 10^9개 이므로 배열로 하게되면 메모리 초과가 날꺼다. 그래서 HashMap을 사용해줬다. 그리고 입력값들은 미리 누적합 배열로 전처리해둔다. (arr[i] = arr[i-1]+cur;)
Map<Integer, Idx> idxes = new HashMap<>();
List<Integer> candidates = new ArrayList<>();
for (int i = 1; i <= n; i++) {
int cur = Integer.parseInt(st.nextToken());
if (!idxes.containsKey(cur)) {
candidates.add(cur);
Idx tmp = new Idx();
idxes.put(cur, tmp);
}
idxes.get(cur).setValue(i);
arr[i] = arr[i - 1] + cur;
}
2. 이제 서로다른 a값들에 대해 최초 인덱스와 마지막 인덱스 사이의 합을 구해서, 그 중 최대치와 최대치의 갯수를 찾아주기만 하면 된다.
long max = 0;
int cnt = 0;
for (int cur : candidates) {
long sum = arr[idxes.get(cur).max] - arr[idxes.get(cur).min-1];
if (sum < max) continue;
if (sum == max) {
cnt++;
continue;
}
max = sum;
cnt = 1;
}
System.out.println(max + " " + cnt);
코드 : github
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in), 1 << 16);
public static void main(String[] args) throws Exception {
new Main().solution();
}
public void solution() throws Exception {
int n = Integer.parseInt(br.readLine());
long[] arr = new long[n + 1];
StringTokenizer st = new StringTokenizer(br.readLine());
Map<Integer, Idx> idxes = new HashMap<>();
List<Integer> candidates = new ArrayList<>();
for (int i = 1; i <= n; i++) {
int cur = Integer.parseInt(st.nextToken());
if (!idxes.containsKey(cur)) {
candidates.add(cur);
Idx tmp = new Idx();
idxes.put(cur, tmp);
}
idxes.get(cur).setValue(i);
arr[i] = arr[i - 1] + cur;
}
long max = 0;
int cnt = 0;
for (int cur : candidates) {
long sum = arr[idxes.get(cur).max] - arr[idxes.get(cur).min-1];
if (sum < max) continue;
if (sum == max) {
cnt++;
continue;
}
max = sum;
cnt = 1;
}
System.out.println(max + " " + cnt);
}
}
class Idx {
int min, max;
public Idx() {
this.min = Integer.MAX_VALUE;
this.max = Integer.MIN_VALUE;
}
public void setValue(int num) {
min = Math.min(min, num);
max = Math.max(max, num);
}
}
'PS > BOJ' 카테고리의 다른 글
[자바] 백준 1477 - 휴게소 세우기 (java) (0) | 2023.12.08 |
---|---|
[자바] 백준 2418 - 단어 격자 (java) (0) | 2023.12.08 |
[자바] 백준 20500 - Ezreal 여눈부터 가네 ㅈㅈ(java) (0) | 2023.12.05 |
[자바] 백준 1612 - 가지고 노는 1 (java) (0) | 2023.12.04 |
[자바] 백준 1584 - 게임 (java) (0) | 2023.12.01 |
댓글