Post

BOJ_3020_개똥벌레 (Java)

BOJ_3020_개똥벌레 (Java)

[Gold V] 개똥벌레 - 3020

문제 링크

성능 요약

메모리: 56196 KB, 시간: 276 ms

분류

이분 탐색, 누적 합

제출 일자

2025년 2월 24일 18:33:26

문제 설명

개똥벌레 한 마리가 장애물(석순과 종유석)로 가득찬 동굴에 들어갔다. 동굴의 길이는 N미터이고, 높이는 H미터이다. (N은 짝수) 첫 번째 장애물은 항상 석순이고, 그 다음에는 종유석과 석순이 번갈아가면서 등장한다.

아래 그림은 길이가 14미터이고 높이가 5미터인 동굴이다. (예제 그림)

이 개똥벌레는 장애물을 피하지 않는다. 자신이 지나갈 구간을 정한 다음 일직선으로 지나가면서 만나는 모든 장애물을 파괴한다.

위의 그림에서 4번째 구간으로 개똥벌레가 날아간다면 파괴해야하는 장애물의 수는 총 여덟개이다. (4번째 구간은 길이가 3인 석순과 길이가 4인 석순의 중간지점을 말한다)

하지만, 첫 번째 구간이나 다섯 번째 구간으로 날아간다면 개똥벌레는 장애물 일곱개만 파괴하면 된다.

동굴의 크기와 높이, 모든 장애물의 크기가 주어진다. 이때, 개똥벌레가 파괴해야하는 장애물의 최솟값과 그러한 구간이 총 몇 개 있는지 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N과 H가 주어진다. N은 항상 짝수이다. (2 ≤ N ≤ 200,000, 2 ≤ H ≤ 500,000)

다음 N개 줄에는 장애물의 크기가 순서대로 주어진다. 장애물의 크기는 H보다 작은 양수이다.

출력

첫째 줄에 개똥벌레가 파괴해야 하는 장애물의 최솟값과 그러한 구간의 수를 공백으로 구분하여 출력한다.

문제풀이

누적합으로 풀었다. 종유석과 석순 각각 누적합으로 겹치는 공간의 숫자를 세주었다. 그 개수별 map을 만들었고 value에는 그 구간들을 넣어주었다. 그래서 최소 부수는 횟수를 기억하고있다가 마지막에 그 key로 찾아가 들어있는 구간들을 포함하는 list의 크기를 구해주었다.

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package BOJ_3020_개똥벌레;

/**
 * Author: nowalex322, Kim HyeonJae
 */

import java.io.*;
import java.util.*;

public class Main {
    static BufferedReader br;
    static BufferedWriter bw;
    static StringTokenizer st;
    static StringBuilder sb = new StringBuilder();
    static int N, H, nums[], odd[], even[], count[], min = Integer.MAX_VALUE;
    static Map<Integer, List<Integer>> breakMap = new HashMap<>(); // key : 부수는횟수, value : 해당 구간들
    public static void main(String[] args) throws Exception {
        new Main().solution();
    }

    public void solution() throws Exception {
//        br = new BufferedReader(new InputStreamReader(System.in));
        br = new BufferedReader(new InputStreamReader(new FileInputStream("src/main/java/BOJ_3020_개똥벌레/input.txt")));
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        H = Integer.parseInt(st.nextToken());
        nums = new int[N+1];
        odd = new int[H+1]; // 홀수번째(석순) 누적합
        even = new int[H+1]; // 짝수번째(종유석) 누적합
        count = new int[H+1]; // 구간별 부술 개수 누적합배열

        for(int i = 1; i <= N; i++) {
            nums[i] = Integer.parseInt(br.readLine());
        }

        for(int i=1; i<=N; i++){
            if(i%2 == 1) odd[nums[i]]++;
            else even[(H+1) - nums[i]]++;
        }
//        System.out.println("odd : " + Arrays.toString(odd));
//        System.out.println("even : " + Arrays.toString(even));
//        System.out.println("처음까지임");
        // 홀수는 끝부터 누적합
        for(int i=H-1; i>=1; i--){
            odd[i] += odd[i+1];
        }
        // 짝수는 처음부터 누적합
        for(int i=2; i<=H; i++){
            even[i] += even[i-1];
        }

        for(int i=1; i<=H; i++){
            count[i] = odd[i] + even[i];
        }

        for(int i=1; i<=H; i++){
            if(!breakMap.containsKey(count[i])) breakMap.put(count[i], new ArrayList<>());
            breakMap.get(count[i]).add(i);
            if(min > count[i]) min = count[i];
        }
//        System.out.println("odd : " + Arrays.toString(odd));
//        System.out.println("even : " + Arrays.toString(even));
//        System.out.println("count : " + Arrays.toString(count));
        sb.append(min).append(" ").append(breakMap.get(min).size());
        System.out.println(sb);
        bw.flush();
        bw.close();
        br.close();
    }
}
This post is licensed under CC BY 4.0 by the author.