본문 바로가기
PS/BOJ

[자바] 백준 2015 - 수들의 합 4 (java)

by Nahwasa 2024. 4. 3.

목차

    문제 : boj2015

     

     

    필요 알고리즘

    • 누적 합 (prefix sum), 해시를 사용한 집합과 맵 (hashmap)
      • 누적합과 해시를 사용해 풀 수 있는 문제이다.

    ※ 제 코드에서 왜 main 함수에 로직을 직접 작성하지 않았는지, 왜 Scanner를 쓰지 않고 BufferedReader를 사용했는지 등에 대해서는 '자바로 백준 풀 때의 팁 및 주의점' 글을 참고해주세요. 백준을 자바로 풀어보려고 시작하시는 분이나, 백준에서 자바로 풀 때의 팁을 원하시는 분들도 보시는걸 추천드립니다.

     

     

    풀이

       문제에서 A배열에서 1부터 X번까지의 누적합을 f(x)라 해보자. 즉 f(x)는 다음과 같다.

     

      이 때 1 <= i <= j <= N인 정수에 대한 A[i]부터 A[j]까지의 합은 f(j) - f(i-1) 이라고 할 수 있다 (혹시 이해가 안된다면 '누적 합(prefix sum), 2차원 누적합(prefix sum of matrix) with java' 글을 확인해보자.).

     

      그렇다면 이 문제에서 입력을 받을 때, A[1], A[2],... 이 아니라 f(1), f(2), ... 을 미리 저장해둔다고 해보자. 즉 입력받으면서 누적합 배열을 미리 만드는거다. 그럼 '부분합 중 합이 K인 것이 몇 개나 있는지' 라는 이 문제에서 물어보는 것은, '배열을 차례대로 보고 있을 때, 현재 x번째를 보고 있다면 배열에서 이전에 f(x) - k가 몇 번 나왔는가?' 로 바꿔서 풀 수 있게 된다. ( f(x) - 찾는값 = k 이므로, 찾는값 = f(x) - k)

     

      그럼 로직은 다음과 같다.

    1. 누적합 배열을 만든다.

    2. 누적합 배열을 하나씩 보면서, 현재 x번째를 보고 있다면 arr[x] - K 가 이전까지 몇 번 나왔는지를 답에 더한다.

    3. arr[x]가 나왔다고 카운팅한다.

    for (int i = 1; i <= n; i++) {
        arr[i] = arr[i-1] + Long.parseLong(st.nextToken());	// [1]
    }
    
    long answer = 0l;
    Map<Long, Long> cnt = new HashMap<>();
    cnt.put(0l, 1l);
    for (int i = 1; i <= n; i++) {
        answer += cnt.getOrDefault(arr[i]-k, 0l);	// [2]
        cnt.put(arr[i], cnt.getOrDefault(arr[i], 0l) + 1);	//[3]
    }
    System.out.println(answer);	// 답 출력

     

     

     

    코드 : github

    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.StringTokenizer;
    
    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 {
            StringTokenizer st = new StringTokenizer(br.readLine());
            int n = Integer.parseInt(st.nextToken());
            long k = Long.parseLong(st.nextToken());
            long[] arr = new long[n+1];
            st = new StringTokenizer(br.readLine());
            for (int i = 1; i <= n; i++) {
                arr[i] = arr[i-1] + Long.parseLong(st.nextToken());
            }
    
            long answer = 0l;
            Map<Long, Long> cnt = new HashMap<>();
            cnt.put(0l, 1l);
            for (int i = 1; i <= n; i++) {
                answer += cnt.getOrDefault(arr[i]-k, 0l);
                cnt.put(arr[i], cnt.getOrDefault(arr[i], 0l) + 1);
            }
            System.out.println(answer);
        }
    }

     

    댓글