CodingTest/Baekjoon

[백준 1939] - 중량제한(Kotlin)[골드3]

재한 2024. 4. 14. 00:17

문제

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

 

1939번: 중량제한

첫째 줄에 N, M(1 ≤ M ≤ 100,000)이 주어진다. 다음 M개의 줄에는 다리에 대한 정보를 나타내는 세 정수 A, B(1 ≤ A, B ≤ N), C(1 ≤ C ≤ 1,000,000,000)가 주어진다. 이는 A번 섬과 B번 섬 사이에 중량제한이

www.acmicpc.net

 

문제 풀이

출발지부터 도착지점까지 옮길 수 있는 중량의 최댓값을 구하는 문제이다.

해당 문제를 풀 수 있는 방법은 여러가지가 있다.

프림과 크루스칼을 통해서 start -> end까지의 최소 비용을 계산하는 방법도 있고,

이분탐색을 통해서 중량을 설정하고, 옮길 수 있는지에 대한 여부를 통해 mid 값을 조절하는 방법이 있다.

나는 이분탐색을 통해서 해당 문제를 풀었다.

 

간선 저장하기

처음 풀이에는 n*n 배열의 간선의 가중치를 저장했지만, 메모리 초과를 확인할 수 있었다.

메모리 초과를 피하기 위해서 인접행렬을 사용해 간선의 정보를 저장했다.

인접행렬에 원소에는 정점과 가중치가 필요로 했기에, 추가적으로 데이터 클래스를 구현했다.

data class Edge(val vertex: Int, val weight: Long)
private lateinit var graph: Array<ArrayList<Edge>>

 

 

graph [i]의 Edge는 정점 i에서 Edge의 vertex까지의 가중치가 weight를 의미한다.

repeat(m) {
    br.readLine().split(" ").map { it.toInt() }.apply {
        graph[this[0]].add(Edge(this[1], this[2].toLong()))
        graph[this[1]].add(Edge(this[0], this[2].toLong()))
        maxWeight = max(maxWeight, this[2].toLong())
    }
}

입력과정에서 양방향 그래프이기 때문에 다음과 같이 넣어줬다.

그리고 이분탐색의 최댓값을 설정하기 위해 maxWeight를 계속 구해줘서 뒤 이분탐색을 실행할 때, 최적화를 했다.

 

이분탐색으로 거리 조절하기

private fun solve(): Long {
    var left = 1L
    var right = maxWeight
    while (left <= right) {
        val mid: Long = (left + right) / 2
        //운반할 수 있는지 없는지 찾아야 함
        if (isTransfer(mid)) { //배달가능
            left = mid + 1
        } else { // 배달 불가능
            right = mid - 1
        }
    }
    return right
}

우리는 출발지에서 도착지점까지 옮길 수 있는 중량을 찾기 위해서 나는 이분탐색을 선택했다.

시간의 여유가 있다면 각 엣지마다의 무게를 전체 리스트로 사용해서 검사할 수 있지만, 해당 방법은 시간이 오래 걸린다.

그러기 위해서 중량을 선택하는 과정을 logN 알고리즘인 이분탐색으로 결정하는 것을 택했다.

while문이 종료된다면, 중량에 대한 탐색이 끝났다는 의미이고, 그때의 right값을 반환한다.

여기서 right는 운반할 수 있는 최대의 중량을 의미한다.

 

옮길 수 있는지 체크하기

private fun isTransfer(weight: Long): Boolean {
    val q = arrayListOf<Int>()
    val visit = BooleanArray(n + 1)
    visit[startFactory] = true
    q.add(startFactory)
    while (q.isNotEmpty()) {
        val now = q.removeFirst()
        for (i in 0 until graph[now].size) {
            val nowVertex = graph[now][i]
            if (nowVertex.weight < weight || visit[nowVertex.vertex]) { //비용보다 비싸다면 no
                continue
            }
            if(nowVertex.vertex==endFactory){
                return true
            }
            visit[nowVertex.vertex] = true
            q.add(nowVertex.vertex)
        }
    }
    return false
}

출발지부터 도착지까지 옮길 수 있는지 체크하는 것은 BFS를 사용했다.

옮길 수 있는 조건은 최대 중량인 weight를 통해서 해당 간선의 가중치가 weight 이하인 정점들만 큐에 넣어줬다.

단순하게 도착할 수 있는지, 아닌지만 확인하면 되기 때문에 도착지점을 발견한다면 true를 반환하도록 했다.

 

 

전체 코드

import kotlin.math.max
import kotlin.math.min

class Solution {

    private val br = System.`in`.bufferedReader()
    private var n = 0
    private var m = 0
    private lateinit var graph: Array<ArrayList<Edge>>
    private var maxWeight = 0L
    private var minWeight = 1000000000L
    private var startFactory = 0
    private var endFactory = 0
    fun run() {
        input()
        print(solve())
    }

    private fun input() {
        br.readLine().split(" ").map { it.toInt() }.apply {
            n = this[0]
            m = this[1]
        }
        graph = Array(n + 1) { ArrayList<Edge>() }
        repeat(m) {
            br.readLine().split(" ").map { it.toInt() }.apply {
                graph[this[0]].add(Edge(this[1], this[2].toLong()))
                graph[this[1]].add(Edge(this[0], this[2].toLong()))
                maxWeight = max(maxWeight, this[2].toLong())
            }
        }
        br.readLine().split(" ").map { it.toInt() }.apply {
            startFactory = this[0]
            endFactory = this[1]
        }
    }

    private fun solve(): Long {
        var left = 1L
        var right = maxWeight
        while (left <= right) {
            val mid: Long = (left + right) / 2
            //운반할 수 있는지 없는지 찾아야 함
            if (isTransfer(mid)) { //배달가능
                left = mid + 1
            } else { // 배달 불가능
                right = mid - 1
            }
        }
        return right
    }

    private fun isTransfer(weight: Long): Boolean {
        val q = arrayListOf<Int>()
        val visit = BooleanArray(n + 1)
        visit[startFactory] = true
        q.add(startFactory)
        while (q.isNotEmpty()) {
            val now = q.removeFirst()
            for (i in 0 until graph[now].size) {
                val nowVertex = graph[now][i]
                if (nowVertex.weight < weight || visit[nowVertex.vertex]) { //비용보다 비싸다면 no
                    continue
                }
                if(nowVertex.vertex==endFactory){
                    return true
                }
                visit[nowVertex.vertex] = true
                q.add(nowVertex.vertex)
            }
        }
        return false
    }

    data class Edge(val vertex: Int, val weight: Long)
}

fun main() {
    val solution = Solution().run()
}