본문 바로가기

백준 PS일지/Dijkstra

[백준/Swift] 1238 : 파티/ 우선순위 큐 개념 정리, 최소 힙 개념 with 라이노님 Heap 정리

문제

 

1238번: 파티

첫째 줄에 N(1 ≤ N ≤ 1,000), M(1 ≤ M ≤ 10,000), X가 공백으로 구분되어 입력된다. 두 번째 줄부터 M+1번째 줄까지 i번째 도로의 시작점, 끝점, 그리고 이 도로를 지나는데 필요한 소요시간 Ti가 들어

www.acmicpc.net

간단한 문제 요약

 각 마을에는 한명의 학생이 살고 있습니다. n명의 학생은 x번 마을에 모여 파티를 합니다. 이때 m개의 도로는 단 방향입니다. i 비용을 들여 도로를 지나갑니다.

 

고려해야 할 사항

  • N, M, X 입력은 1 부터입니다.. (X제약을 못봐서 계속 X위치를 다르게 구했다는..ㅠㅠ)

문제 풀이, 했갈렸던 점

 x번을 제외한 마을 학생들은 각자가 갈 수있는 모든 마을에 대해 최단 경로를 구한 후 x일때의 최단 경로를 저장합니다. 이후 x번에서 갈 수있는 최단 경로를 추가로 구한 후 이전에 구했던 최단 경로와 더한 후 max값을 찾으면 됩니다. 한 가지 했갈렸던 점은 x번의 마을에 있는 학생은 어떻게 해야하는지에 대한 고민이 있었는데 x번 학생은 x번 마을에 그대로 있는 것이었습니다.

코드

// 힙 구조는 아래에 있습니다.

struct Node {
    let vertex: Int
    let cost: Int
}
extension Node: Equatable, Comparable {
    static func <(lhs: Node, rhs: Node) -> Bool {
        return lhs.cost < rhs.cost
    }
}

//MARK: - Properties
var input = readLine()!.split{$0==" "}.map{Int(String($0))!}
let (n,m,x) = (input[0],input[1],input[2] - 1)
var graph = Array(repeating: [Node](), count: n)

var res = Array(repeating: 0, count: n)
var lastDist = res
(0..<m).forEach { _ in
    let temp = readLine()!.split{$0==" "}.map{Int(String($0))!}
    let (s,e,c) = (temp[0]-1,temp[1]-1,temp[2])
    graph[s].append(Node(vertex: e, cost: c))
}

func dijkstra(_ start: Int, _ last: Bool = false) {
    var dist = Array(repeating: Int.max, count: n)
    dist[start] = 0
    var priority = Heap<Node>()
    priority.insert(Node(vertex: start, cost: 0))
    while let curNode = priority.delete() {
        if dist[curNode.vertex] < curNode.cost { continue }
        for node in graph[curNode.vertex] {
            if dist[curNode.vertex] + node.cost < dist[node.vertex] {
                dist[node.vertex] = dist[curNode.vertex] + node.cost
                priority.insert(Node(vertex: node.vertex, cost: dist[node.vertex]))
            }
        }
    }
    guard last else {
        res[start] = dist[x]
        return
    }
    lastDist = dist
}

func solution() {
    (0..<n).forEach { dijkstra($0) }
    dijkstra(x,true)
    (0..<res.count).forEach { res[$0] += lastDist[$0] }
    print(res.max()!)
}

solution()

라이노님 힙 구조 조금 변형

struct Heap<T> where T: Comparable {
    private var heap = [T]()
    let compare: (T,T) -> Bool
    init(compare: @escaping (T,T) -> Bool) {
        self.compare = compare
    }
    init() {
        self.init(compare: <)
    }
}
extension Heap {
    var isEmpty: Bool {
        return heap.isEmpty
    }
    mutating func insert(_ element: T) {
        var idx = heap.count
        heap.append(element)
        while idx > 0 && compare(heap[idx], heap[(idx-1)/2]) {
            heap.swapAt((idx-1)/2, idx)
            idx = (idx-1)/2
        }
    }
    mutating func delete() -> T? {
        guard !heap.isEmpty else {
            return nil
        }
        guard heap.count != 1 else {
            return heap.removeFirst()
        }
        let res = heap.first!
        heap.swapAt(0, heap.count-1)
        _ = heap.removeLast()
        var idx = 0
        while idx < heap.count {
            let (left,right) = (idx*2+1,idx*2+2)
            if right < heap.count {
                if compare(heap[left], heap[right]) && compare(heap[left], heap[idx]) {
                    heap.swapAt(left, idx)
                    idx = left
                }else if compare(heap[right],heap[idx]) {
                    heap.swapAt(right, idx)
                    idx = right
                }else {
                    break
                }
            }else if left < heap.count {
                if compare(heap[left], heap[idx]) {
                    heap.swapAt(left, idx)
                    idx = left
                }else {
                    break
                }
            }else {
                break
            }
        }
        return res
    }
}

라이노님 github