본문 바로가기
알고리즘 문제 풀이/백준

백준 3020 개똥벌레 Kotlin (이분 탐색)

by 옹구스투스 2022. 1. 1.
반응형

문제 출처 : https://www.acmicpc.net/problem/3020

 

3020번: 개똥벌레

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

www.acmicpc.net

문제

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

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

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

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

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

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

입력

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

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

출력

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

문제

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

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

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

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

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

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

입력

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

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

출력

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

알고리즘 분류

풀이

이분 탐색에 근거한 upper_bound, lower_bound에 대해 안다면 쉽게 풀 수 있다.

upper_bound, lower_bound는 c++의 algorithm 헤더에 있지만, Kotlin에선 직접 구현하여 사용해야 한다.

upper_bound : 정렬된 리스트에서 찾는 값보다 큰 값의 최초 인덱스

lower_bound : 정렬된 리스트에서 찾는 값의 최초 인덱스

upper_bound, lower_bound 모두, 만약 찾는 값이 없다면 찾는 값보다 큰 값의 최초 인덱스를 반환한다.

석순은 lower_bound, 종유석은 upper_bound를 사용하면 된다.

이때 종유석은 거꾸로 되어있으니 구간을 검색할 때 h - curHigh로 해야 한다.

 

본인은 석순과 종유석을 따로 저장해서, 오름차순으로 정렬하였다.

upper_bound 대신 lower_bound에 curHigh+1을 검색하였고,

모든 구간에 대한 장애물을 부시는 최솟값과 구간의 수를 갱신하면 된다.

 

코드

val INF = 987654321
val br = System.`in`.bufferedReader()
// 2<= n <=200000
// 2<= h <=500000
fun main() = with(System.out.bufferedWriter()){
    val (n,h) = br.readLine().split(' ').map{it.toInt()}
    //n은 항상 짝수
    val arr = IntArray(n/2)
    val reverseArr = IntArray(n/2)
    for(i in 0 until n){
        if(i and 1 ==1){
            reverseArr[i/2] = br.readLine().toInt()
        }
        else{
            arr[i/2] = br.readLine().toInt()
        }
    }
    arr.sort()
    reverseArr.sort()

    var result = INF
    var answer=0
    for(high in 1 .. h){
        var forVal = n/2-lowerBound(0,n/2,arr,high)
        var reverseVal = n/2-lowerBound(0,n/2,reverseArr,h+1-high)
        if(result==forVal+reverseVal){
            answer++
        }
        else if(result > forVal+reverseVal){
            result = forVal+reverseVal
            answer=1
        }
    }
    write("$result $answer")
    close()
}
fun lowerBound(start : Int, end : Int, arr : IntArray,high : Int) : Int{
    var s = start
    var e = end

    while(s<e){
        val mid = (s+e)/2

        if(arr[mid]>=high){
            e =mid
        }
        else{
            s=mid+1
        }
    }
    return e
}
반응형

댓글