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

백준 1238 파티 Kotlin (다익스트라)

by 옹구스투스 2021. 8. 21.
반응형

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

 

1238번: 파티

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

www.acmicpc.net

문제

N개의 숫자로 구분된 각각의 마을에 한 명의 학생이 살고 있다.

어느 날 이 N명의 학생이 X (1 ≤ X ≤ N)번 마을에 모여서 파티를 벌이기로 했다. 이 마을 사이에는 총 M개의 단방향 도로들이 있고 i번째 길을 지나는데 Ti(1 ≤ Ti ≤ 100)의 시간을 소비한다.

각각의 학생들은 파티에 참석하기 위해 걸어가서 다시 그들의 마을로 돌아와야 한다. 하지만 이 학생들은 워낙 게을러서 최단 시간에 오고 가기를 원한다.

이 도로들은 단방향이기 때문에 아마 그들이 오고 가는 길이 다를지도 모른다. N명의 학생들 중 오고 가는데 가장 많은 시간을 소비하는 학생은 누구일지 구하여라.

입력

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

모든 학생들은 집에서 X에 갈수 있고, X에서 집으로 돌아올 수 있는 데이터만 입력으로 주어진다.

출력

첫 번째 줄에 N명의 학생들 중 오고 가는데 가장 오래 걸리는 학생의 소요시간을 출력한다.

알고리즘 분류

풀이

문제에서 요구하는 것은 어떠한 점에서 어떠한 점으로 가는 최단 경로이다.

여기서 플로이드 와샬, 다익스트라 알고리즘을 생각할 수 있는데,

주어진 입력이 N=1000이기 때문에 O(N^3)인 플로이드 와샬로 풀면 시간 초과가 뜰 것이다.

 

다익스트라를 이용한 풀이는 다음과 같다.

1~n 마을에서 k마을에 도착하는 최단 경로 + k마을에서 1~n마을에 도착하는 최단 경로 중 최댓값을 구하면 되는데,

이는 두 해를 각각 n번의 다익스트라로 구할 수 있다.

 

이대로 제출해도 통과되지만 다익스트라를 2n번 돌리기 때문에 입력이 좀 더 큰 경우 시간 초과가 날 것이다.

다익스트라를 많이 돌리면 결국 플로이드 와샬과 미세한 차이밖에 나지 않으므로, 우리는 이 다익스트라를 조금 돌리는 방법을 생각해 내야 한다.

 

주어진 그래프에서

1~n 마을에서 k마을에 도착하는 최단 경로를 a라 하고

k마을에서 1~n마을에 도착하는 최단 경로를 b라고 하자.

b는 입력으로 주어진 간선대로 k에 대해 다익스트라를 돌리면 찾을 수 있다.

위의 풀이에서 a 때문에 n번 다익스트라를 돌렸었는데, 

1 -> k 의 경로 값이 5라고 하자.

이는 1마을에서 k마을에 도착하는 최단 경로이다.

이번엔 간선을 뒤집어서 k -> 1 의 경로 값이 5라고 하자.

이는 k마을에서 1마을에 도착하는 최단 경로이다.

즉 주어진 간선을 뒤집어서 다익스트라를 k에서 돌리면

a값인 1~n 마을에서 k망르에 도착하는 최단 경로를 한 번의 다익스트라로 구할 수 있다.

이렇게 총 두 번의 다익스트라로 답을 구하는 것이 정해인 것 같다.

 

k마을에서 k마을에 도착하는 최단 경로는 예외처리하자!  

코드

import java.util.*
import kotlin.math.*

//1<=n<=1000
//1<=m<=10000
//단방향 그래프
const val INF = 987654321

fun dijkstra(graph : Array<ArrayList<Pair<Int,Int>>>,start : Int, dp : IntArray){
    val pq = PriorityQueue<Pair<Int,Int>>{a,b ->
        when{
            a.first<b.first -> -1
            a.first==b.first ->0
            else -> 1
        }
    }
    pq.add(Pair(0,start))

    while(pq.isNotEmpty()){
        val (dis,cur) = pq.poll()
        if(dp[cur]<dis)continue
        for(i in graph[cur].indices){
            val next = graph[cur][i].first
            val nextDis = dis + graph[cur][i].second
            if(dp[next]<nextDis)continue
            dp[next]=nextDis
            pq.add(Pair(nextDis,next))
        }
    }
}

fun main()= with(System.out.bufferedWriter()){
    val br = System.`in`.bufferedReader()
    var tk = StringTokenizer(br.readLine())
    val (n,m,k) = List(3){Integer.parseInt(tk.nextToken())}
    val graph = Array(n+1){ArrayList<Pair<Int,Int>>()}
    val reverseGraph = Array(n+1){ArrayList<Pair<Int,Int>>()}
    val dp = IntArray(n+1){INF}
    val reverseDp = IntArray(n+1){INF}
    var answer =0
    for(i in 0 until m){
        tk = StringTokenizer(br.readLine())
        val (from, to, dis) = List(3){Integer.parseInt(tk.nextToken())}
        graph[from].add(Pair(to,dis))
        reverseGraph[to].add(Pair(from,dis))
    }
    //k에서 각 마을로 가는 최단 경로
    dijkstra(graph,k,dp)
    //각 마을에서 k까지 가는 최단 경로
    dijkstra(reverseGraph,k,reverseDp)
    for(i in 1 .. n) {
        if(i==k)continue
        answer = max(answer, dp[i]+reverseDp[i])
    }
    write("$answer")
    close()
}
반응형

댓글