본문 바로가기
알고리즘 이전/그리디

다익스트라자 알고리즘

by hoshi03 2023. 9. 5.

가중치 방향그래프에서 1번 정점에서 모든 정점으로의 최소 거리비용을 출력하기, 경로가 없으면 impossible 출력

 

첫째 줄에는 정점의 수 N과 간선의 수 M가 주어진다. 그 다음부터 M줄에 걸쳐 연결정보와 거리비용이 주어진다.

 

입력
6 9
1 2 12 <- 1번에서 2번으로 가는데 12의 비용이 든다
1 3 4
2 1 2
2 3 5
2 5 5
3 4 5
4 2 2
4 5 5
6 4 5

 

출력

2 : 11
3 : 4
4 : 9
5 : 14
6 : impossible

 

음수가 아닌 가중치 방향그래프에서 다익스트라자 알고리즘을 이용해서 최소 거리비용을 찾는 문제

 

정점의 길이만큼 dis 배열을 만들어서 큰 값을 넣어두고, 처음에 넣어둔 큰 값보다 작은 값이 그 정점에 도달하는 거리비용이면 dis 배열의 값을 갱신한다, 우선순위 큐를 사용하면 N*N의 시간복잡도를 NlogN으로 개선할수 있다

 

풀이방법

 

먼저 정점의 정보를 저장할 클래스를 만든다 , 각 정점은 정점의 번호와 그 정점까지 가는데 걸리는 비용으로 이루어진다

우선순위 큐에 넣을때 작은 비용부터 오름차순으로 나오게 Comparable을 써서 비용을 기준으로 오름차순 정렬한다

class Edge implements Comparable<Edge>{
    int vex; //정점
    int cost; //비용
    Edge(int vex, int cost){
        this.vex = vex;
        this.cost = cost;
    }

    @Override
    public int compareTo(Edge o) {
        // 오름차순으로 정렬
        return this.cost - o.cost;
    }
}

한 정점에 연결된 다른 정점을 for문으로 탐색하기 위해 정점을 원소로 가지는 ArrayList를 담을 수 있는 ArrayList를 만든다

    static  ArrayList<ArrayList<Edge>> graph = new ArrayList<ArrayList<Edge>>();
    for (int i =0; i <= n; i++) graph.add(new ArrayList<Edge>());
    for (int i =0; i < m; i++){
            int a = in.nextInt();
            int b = in.nextInt();
            int c = in.nextInt();
            //a번 정점에서 b번 정점까지 가는데 c만큼 비용이 든다는걸 저장
            graph.get(a).add(new Edge(b,c));
        }

 

정점까지의 거리를 저장할 dis배열을 만들고, 큰 값으로 초기화한다

dis = new int[n+1];
Arrays.fill(dis,Integer.MAX_VALUE);

 

다익스트라자 알고리즘으로 최소거리를 구하는 함수

pq에서 꺼내지는 값은 방문 비용이 가장 작은 값이다

 for문을 돌면서 정점들을 탐색하며 dis값보다 now 노드를 거쳐서 정점에 도달하는 거리비용이 작으면 dis값을 갱신한다

    public void Solution(int v){
        //꺼내지는 기준은 edge를 만들때 해둔 오름차순을 기준으로
        PriorityQueue<Edge> pq = new PriorityQueue<>();
        //시작하는 노드 1을 넣어주고
        pq.offer(new Edge(1,0));
        //당연히 시작 노드까지 가는 비용은 0이다
        dis[v] = 0;
        while (!pq.isEmpty()){
            Edge tmp = pq.poll();
            int now = tmp.vex;
            int nowCost = tmp.cost;
            //nowCost가 dis[now] 보다 크면, 비용이 정점을 더 거쳐서 비용이 더 들 수 밖에 없으니 건너뛴다
            if (nowCost > dis[now]) continue;
            //now와 연결된 정점들을 순회하면서 최소 비용을 탐색
            for (Edge ob : graph.get(now)){
                //현재 정점과 연결된 정점의 비용을 기존 연결된 정점의 비용이 dis 보다 작으면, 처음에 dis가 max로 초기화되었기에
                //방문한 정점이면 작아질수 밖에 없음
                 if (dis[ob.vex] > nowCost + ob.cost){
                     dis[ob.vex] = nowCost + ob.cost;
                     //pq에 현재 정점과 연결된 정점의 비용을 기존 연결된 정점의 비용과 합쳐서 넣음
                     pq.offer(new Edge(ob.vex, ob.cost + nowCost));
                 }
            }
        }
    }

전체 코드

import java.util.*;

class Edge implements Comparable<Edge>{
    int vex; //정점
    int cost; //비용
    Edge(int vex, int cost){
        this.vex = vex;
        this.cost = cost;
    }

    @Override
    public int compareTo(Edge o) {
        // 오름차순으로 정렬
        return this.cost - o.cost;
    }
}


class Main{

    static  int n;
    static  ArrayList<ArrayList<Edge>> graph;
    static int[] dis; //최소거리를 구하기 위해서 사용하는 배열

    public void Solution(int v){
        //꺼내지는 기준은 edge를 만들때 해둔 오름차순을 기준으로
        PriorityQueue<Edge> pq = new PriorityQueue<>();
        //시작하는 노드 1을 넣어주고
        pq.offer(new Edge(1,0));
        dis[v] = 0;
        while (!pq.isEmpty()){
            Edge tmp = pq.poll();
            int now = tmp.vex;
            int nowCost = tmp.cost;
            //nowCost가 dis[now] 보다 크면, 비용이 정점을 더 거쳐서 비용이 더 들 수 밖에 없으니 건너뛴다
            if (nowCost > dis[now]) continue;
            //now와 연결된 정점들을 순회하면서 최소 비용을 탐색
            for (Edge ob : graph.get(now)){
                //현재 정점과 연결된 정점의 비용을 기존 연결된 정점의 비용이 dis 보다 작으면, 처음에 dis가 max로 초기화되었기에
                //방문한 정점이면 작아질수 밖에 없음
                 if (dis[ob.vex] > nowCost + ob.cost){
                     dis[ob.vex] = nowCost + ob.cost;
                     //pq에 현재 정점과 연결된 정점의 비용을 기존 연결된 정점의 비용과 합쳐서 넣음
                     pq.offer(new Edge(ob.vex, ob.cost + nowCost));
                 }
            }


        }
    }

    public static void main(String[] args) {
        Main T = new Main();
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        graph = new ArrayList<ArrayList<Edge>>();
        //0~n 번까지 Edge를 넣을수 있는 ArrayList를 만들어두고 1번부터 N번까지 N개를 사용함, 0번부터 넣어주니 0 <= N
        for (int i =0; i <= n; i++) graph.add(new ArrayList<Edge>());
        dis = new int[n+1];
        //dis 배열에 최대값을 넣어서 초기화, 나중에 값이 변하지 안았으면 도달 불가능한 정점
        Arrays.fill(dis,Integer.MAX_VALUE);
        for (int i =0; i < m; i++){
            int a = in.nextInt();
            int b = in.nextInt();
            int c = in.nextInt();
            //a번 정점에서 b번 정점까지 가는데 c만큼 비용이 든다는걸 저장
            graph.get(a).add(new Edge(b,c));
        }

        T.Solution(1);
        for (int i =2; i <=n ; i++){
            if (dis[i] != Integer.MAX_VALUE) System.out.println(i + " : " + dis[i]);
            else System.out.println(i + " : " + "impossible");
        }

    }
}

 

'알고리즘 이전 > 그리디' 카테고리의 다른 글

원더랜드(최소스패닝트리-크루스칼)  (2) 2023.09.06
친구인가(Disjoint-set: 유니온-파인드)  (0) 2023.09.06
최대 수입 스케쥴(우선순위 큐)  (0) 2023.09.04
결혼식  (0) 2023.09.04
회의실 배정  (0) 2023.09.04