ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 백준 1715 카드 정렬하기
    Algorithm/BOJ 2021. 4. 7. 12:57
    728x90

    출처: www.acmicpc.net/problem/1715

    분류: 우선순위 큐, 힙


    접근방식

    가장 최소인 값 두 개를 계속 더해가면 되는 문제입니다.

    문제는 어떻게 최소 두 개를 찾을지가 관건이네요. 

    역시 최대, 최소 하면 제일 먼저 떠오르는 건 이겠죠? 힙으로 풀어봤습니다.

    Heap 에 대해 궁금하시다면 여기로

    다른 분들 풀이를 보니 이분탐색으로 풀어도 아슬아슬하게 통과는 되는 것 같더라구요.

     

     

    해결방법

    public struct Heap<T> {
        
        private var nodes = [T]()
        
        private var orderCriteria: (T, T) -> Bool
        
        public init(sort: @escaping (T, T) -> Bool) {
            // 최대 힙인지 최소 힙인지 기준을 잡는다.
            self.orderCriteria = sort
        }
        
        public init(array: [T], sort: @escaping (T, T) -> Bool) {
            self.orderCriteria = sort
            configureHeap(from: array)
        }
        
        public var count: Int {
            return nodes.count
        }
        
        public func peek() -> T? {
            return nodes.first
        }
        
        public mutating func insert(_ value: T) {
            nodes.append(value)
            shiftUp(nodes.count - 1)
        }
        
        public mutating func remove() -> T? {
            guard !nodes.isEmpty else { return nil }
            
            if nodes.count == 1 {
                return nodes.removeLast()
            } else {
                let value = nodes[0]
                nodes[0] = nodes.removeLast()
                shiftDown(0)
                return value
            }
        }
        
        public mutating func remove(at index: Int) -> T? {
            guard index < nodes.count else { return nil }
            
            let lastIndex = nodes.count-1
            if index != lastIndex {
                nodes.swapAt(index, lastIndex)
                shiftDown(from: index, until: lastIndex)
                shiftUp(index)
            }
            
            return nodes.removeLast()
        }
        
        // 변수를 직접 변경해야 하기 때문에 mutating 을 사용한다.
        private mutating func configureHeap(from array: [T]) {
            nodes = array
            
            /**
             * 힙 트리에서 n/2 - 1 은 하나 위 층위가 된다.
             * shiftDown을 하면서 자리를 잡을 것이기 때문에
             * 마지막 leaf 노드들은 할 필요가 없다.
             */
            for i in stride(from: nodes.count/2 - 1, through: 0, by: -1) {
                shiftDown(i)
            }
        }
        
        private func parentIndex(ofIndex i: Int) -> Int {
            return (i - 1) / 2
        }
        
        private func leftChildIndex(ofIndex i: Int) -> Int {
            return 2*i + 1
        }
        
        private func rightChildIndex(ofIndex i: Int) -> Int {
            return 2*i + 2
        }
        
        /**
         * shiftUp은 자신과 부모를 비교해 바꿔준다.
         */
        private mutating func shiftUp(_ index: Int) {
            var childIndex = index
            let child = nodes[childIndex] // 처음에 노드를 저장해두고 인덱스를 구한 후 바꿔준다.
            var parentIndex = self.parentIndex(ofIndex: index)
            
            while childIndex > 0 && orderCriteria(child, nodes[parentIndex]) {
                nodes[childIndex] = nodes[parentIndex]
                childIndex = parentIndex
                parentIndex = self.parentIndex(ofIndex: childIndex)
            }
            
            nodes[childIndex] = child
        }
        
        /**
         * shiftDown은 left, right 자식 중 적합한 녀석이 있으면 바꾸면서 바닥까지 내려간다.
         */
        private mutating func shiftDown(from index: Int, until endIndex: Int) {
            let leftChildIndex = self.leftChildIndex(ofIndex: index)
            let rightChildIndex = leftChildIndex + 1
            
            var first = index
            if leftChildIndex < endIndex && orderCriteria(nodes[leftChildIndex], nodes[first]) {
                first = leftChildIndex
            }
            if rightChildIndex < endIndex && orderCriteria(nodes[rightChildIndex], nodes[first]) {
                first = rightChildIndex
            }
            if first == index { return }
            
            nodes.swapAt(index, first)
            shiftDown(from: first, until: endIndex)
        }
        
        private mutating func shiftDown(_ index: Int) {
            shiftDown(from: index, until: nodes.count)
        }
        
    }
    
    let n = Int(readLine()!)!
    var numbers = [Int]()
    for _ in 0..<n {
        numbers.append(Int(readLine()!)!)
    }
    
    var heap = Heap<Int>(array: numbers, sort: <)
    var cost = 0
    
    while heap.count > 1 {
        let n1 = heap.remove()!
        let n2 = heap.remove()!
        
        cost += n1 + n2
        heap.insert(n1 + n2)
    }
    
    print(cost)

     

    배운점

    힙... 우선순위 큐.. 안 보고 할 수 있게 구현 연습 좀 하자... ;ㅅ;

     

    'Algorithm > BOJ' 카테고리의 다른 글

    백준 16234 인구 이동  (0) 2021.04.08
    백준 1202 보석 도둑  (0) 2021.04.07
    백준 11404 플로이드  (0) 2021.04.07
    백준 13549 숨바꼭질 3  (0) 2021.04.07
    백준 1916 최소비용 구하기  (0) 2021.04.06

    댓글

Designed by Tistory.