알고리즘 공부/최단거리 알고리즘

[프로그래머스] 환승 택시 요금

kdhoooon 2021. 5. 25. 14:58

문제


지점의 개수 n, 출발지점을 나타내는 s, A의 도착지점을 나타내는 a, B의 도착지점을 나타내는 b, 지점 사이의 예상 택시요금을 나타내는 fares가 매개변수로 주어집니다. 이때, A, B 두 사람이 s에서 출발해서 각각의 도착 지점까지 택시를 타고 간다고 가정할 때, 최저 예상 택시요금을 계산해서 return 하도록 solution 함수를 완성해 주세요.
만약, 아예 합승을 하지 않고 각자 이동하는 경우의 예상 택시요금이 더 낮다면, 합승을 하지 않아도 됩니다.

 

[제한사항]

  • 지점갯수 n은 3 이상 200 이하인 자연수입니다.
  • 지점 s, a, b는 1 이상 n 이하인 자연수이며, 각기 서로 다른 값입니다.
    • 즉, 출발지점, A의 도착지점, B의 도착지점은 서로 겹치지 않습니다.
  • fares는 2차원 정수 배열입니다.
  • fares 배열의 크기는 2 이상 n x (n-1) / 2 이하입니다.
    • 예를들어, n = 6이라면 fares 배열의 크기는 2 이상 15 이하입니다. (6 x 5 / 2 = 15)
    • fares 배열의 각 행은 [c, d, f] 형태입니다.
    • c지점과 d지점 사이의 예상 택시요금이 f원이라는 뜻입니다.
    • 지점 c, d는 1 이상 n 이하인 자연수이며, 각기 서로 다른 값입니다.
    • 요금 f는 1 이상 100,000 이하인 자연수입니다.
    • fares 배열에 두 지점 간 예상 택시요금은 1개만 주어집니다. 즉, [c, d, f]가 있다면 [d, c, f]는 주어지지 않습니다.
  • 출발지점 s에서 도착지점 a와 b로 가는 경로가 존재하는 경우만 입력으로 주어집니다.

 

 

풀이


플로이드 와샬 알고리즘을 이용하였다.

 

알고리즘 순서는 아래와 같다.

  1.  각 노드에서 모든 노드로 가는 최단 경로를 구해둔다.(플로이드 와샬 알고리즘)
  2.  S지점에서 하나씩 노드를 방문하며 해당 노드에서 a 와 b 로가는 각각의 경로를 더해준다.

이렇게 구해준 노드의 경로중 가장 짧은 경로가 답이 된다.

 

플로이드 와샬 알고리즘의 코드다.

for(int k = 1 ; k <= n ; k++){
	for(int i = 1 ; i <= n ;i++){
		for(int j = 1 ; j <= n ; j++){
			if( i == j ) {continue;}
                    
			if(map[i][j] > map[i][k] + map[k][j]){
				map[i][j] = map[i][k] + map[k][j];
			}
		}
	}
}

플로이드 와샬 알고리즘은 반복문을 세번이나 사용하기 때문에 O(n^3)의 좋지 않은 시간 복잡도를 가지고 있지만 모든 노드가 각각의 노드로 갈때의 최단경로를 알 수 있다는 점에서 유용하게 사용할 수 있는 알고리즘이다.

 

여기서 k 는 거쳐가는 노드의 번호, i 는 출발점,  j 는 도착점이 된다.

그래서 map[i][j] 가 map[i][k] + map[k][j] 보다 크다면, i 에서 j 로 바로가는 것보다 i 에서 k 를 거쳐 j 로 가는게 더 최단거리라는 의미가 된다. 

 

이렇게 구해진 최단거리를 통해서 

sum = map[s][i] + map[i][a] + map[i][b];

s 에서 출발하여 i 까지 간뒤 i 에서 a , b 로가는 거리가 가장 짧을 때의 크기가 답이 된다.

 

 

<전체노드>

class Solution {
    
    static int[][] map;
    public int solution(int n, int s, int a, int b, int[][] fares) {
        int answer = 0;
        
        map = new int[n + 1][n + 1];
        
        for(int i = 1 ; i <= n ; i++){
            for(int j = 1 ; j <= n ; j++){
                if(i == j){
                    map[i][j] = 0;
                }
                else{
                    map[i][j] = 1000001;
                }
            }
        }
        
        for(int i = 0 ; i < fares.length ; i++){
            int start = fares[i][0];
            int end = fares[i][1];
            int weight = fares[i][2];
            
            map[start][end] = weight;
            map[end][start] = weight;
        }
        
        for(int k = 1 ; k <= n ; k++){
            for(int i = 1 ; i <= n ;i++){
                for(int j = 1 ; j <= n ; j++){
                    if( i == j ) {continue;}
                    
                    if(map[i][j] > map[i][k] + map[k][j]){
                        map[i][j] = map[i][k] + map[k][j];
                    }
                }
            }
        }
        
        answer = Integer.MAX_VALUE;
        int sum = 0;
        for(int i = 1 ; i <= n ; i++){
            sum = map[s][i] + map[i][a] + map[i][b];
            
            if(answer > sum){
                answer = sum;
            }
        }        
        
        return answer;
    }
}