boj3020 개똥벌레_java

dgh03207·2022년 4월 25일
0

알고리즘

목록 보기
26/45

링크

https://www.acmicpc.net/problem/3020

문제

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

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

Untitled

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

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

Untitled

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

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

입력

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

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

출력

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

나의 풀이

  • 누적합+이분탐색으로 문제를 해결하였다.

  • 높이만큼의 정수배열을 만들어 높이에 해당하는 종유석 혹은 석순의 개수를 저장하는 배열을 만들었다. 종유석과 석순을 A , B 배열로 놓고, 풀었다.

  • 배열에 값을 넣고, 뒤에서부터 누적합을 적용하였다. 누적합을 사용하게된 근본은 1m 3m 5m가 있을때, 1m가 부러지는 높이면 뒷 부분은 무조건 부러지기 때문이다.
    A가 1 0 1 0 1 0 0 이라고 했을때(편의상 배열의 index를 1부터 시작한다고 가정하자)
    7m 에서 자를때는, 1m 3m 5m 짜리가 부러지지않으므로, 0이 맞지만 5m부터 하나씩 부러지기 시작하고, 3m일때는 2개 1m일때는 3개 부러지므로, 거꾸로 누적합을 계산하여, 3 3 2 2 1 0 으로 AB 모두 만들어놓고, 높이별로 합하여, min값을 갱신하여 답을 구하였다 .

  • 핵심 코드

            for (int i = 1; i < H+1; i++) {
                A[H-i] = A[H-i]  +A[H-i+1];
                B[H-i] = B[H-i]  +B[H-i+1];
    
            }
    
         
            for (int i = 1; i < H+1; i++) {
                int broken = A[i]+B[H-i+1];
                if(minBroken>broken){
                    minBroken = broken;
                    answer = 1;
                }else if(minBroken==broken){
                    answer +=1;
                }
            }
  • 전체 코드

    package Baekjoon.java.gold;
    
    import java.io.*;
    import java.util.*;
    
    public class boj3020 {
        public static void main(String[] args) throws Exception{
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            StringTokenizer st = new StringTokenizer(br.readLine());
    
            int N = Integer.parseInt(st.nextToken());
            int H = Integer.parseInt(st.nextToken());
            //A는 석순, B는 종유석
            int[] A = new int[H+1];
            int[] B = new int[H+1];
            for (int i = 0; i < N; i++) {
                if(i%2==0) {
                    B[Integer.parseInt(br.readLine())]+=1;
                }else{
                    A[Integer.parseInt(br.readLine())]+=1;
                }
            }
            int answer = 0;
            int minBroken = Integer.MAX_VALUE;
    
            //누적합 만들기
            for (int i = 1; i < H+1; i++) {
                A[H-i] = A[H-i]  +A[H-i+1];
                B[H-i] = B[H-i]  +B[H-i+1];
    
            }
    
         
            for (int i = 1; i < H+1; i++) {
                int broken = A[i]+B[H-i+1];
                if(minBroken>broken){
                    minBroken = broken;
                    answer = 1;
                }else if(minBroken==broken){
                    answer +=1;
                }
            }
            System.out.println(minBroken+" "+answer);
        }
    }

결과

profile
같이 공부하자!

0개의 댓글