문법/알고리즘

다익스트라 기본문제

코딩 화이팅 2023. 4. 6. 23:45

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

 

1916번: 최소비용 구하기

첫째 줄에 도시의 개수 N(1 ≤ N ≤ 1,000)이 주어지고 둘째 줄에는 버스의 개수 M(1 ≤ M ≤ 100,000)이 주어진다. 그리고 셋째 줄부터 M+2줄까지 다음과 같은 버스의 정보가 주어진다. 먼저 처음에는 그

www.acmicpc.net

package 다익스트라;
    
import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.PriorityQueue;
    import java.util.Scanner;
    
public class 최소비용구하기_1916 {
    static int E,V;//정점과 간선의 수
    static int[] arr;//각 정점에서 다음 정점까지의 최소 가중치 값을 저장하는 배열
    static boolean[] visit;//방문 배열
    static List<Node> list[];//각 정점에 연결된 정점과 가중치 값을 저장할 리스트 배열
    static int max=Integer.MAX_VALUE;//최대값
    
    //Node라는 클래스를 만들어 연결되는 정점과 연결될 때의 가중치 값을 저장할 클래스
    static class Node implements Comparable<Node>{
        int v;//연결될 값 그니까 시작 값이 아닌 끝 값
        int w;//가중치 값
        //멤버변수 선언
        public Node(int v, int w) {
            super();
            this.v = v;
            this.w = w;
        }
        //가중치 값으로 정렬해줌
        @Override
        public int compareTo(Node o) {
            // TODO Auto-generated method stub
            return this.w-o.w;
        }        
        
    }
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        V=sc.nextInt();//정점 수        
        E=sc.nextInt();//가중치 값
        arr=new int[V+1];//1부터 시작할 것이기 때문에 저장해줄 배열도 정점의 수+1
        visit=new boolean[V+1];//1부터 시작할 것이기 때문에 저장해줄 방문 배열도 정점의 수+1        
        list=new ArrayList[V+1];//1부터 시작하기 때문에 리스트 배열도 정점의 수+1
        
        //인접 리스트 배열 안에 리스트 값들을 하나씩 ArrayList선언
        for (int i = 1; i <= V; i++) {
            list[i]=new ArrayList<>();
        }
        
        //각 시작값과 끝값 가중치 값 입력
        for (int i = 1; i <=E; i++) {
            int st=sc.nextInt();
            int ed=sc.nextInt();
            int w=sc.nextInt();
            
            //각 시작값에 끝값과 가중치 값을 더해 정점과 이어지는 정점과 가중치 값을 시작 값에 저장
            list[st].add(new Node(ed,w));
        }
        
        int start=sc.nextInt();//출발점 입력
        int end=sc.nextInt();//도착점 입력
        Arrays.fill(arr, max);//각 저장 배열에 정점들에 큰 값들 입력
        dijkstra(start);//다익스트라 알고리즘 시작
        System.out.println(arr[end]);
    }
    
    private static void dijkstra(int end) {
        PriorityQueue<Node> pq=new PriorityQueue<>();//다익에서 필수인 priorityQueue 선언
        pq.add(new Node(end,0));//pq큐에 처음에 만든 Node클래스에 끝값과 가중치 값인 객체를 만들고
        //그 객체의 끝 값과 가중치 값을 큐에 더해준다.
        arr[end]=0;//일단 while에 들어가기 전이기 때문에 첫 값이다.그래서 그 값을 0으로 바꿔준다.시작 점이라는 의미
        while (!pq.isEmpty()) {//이제 큐가 비기 전까지 즉 끝까지 반복
            Node now=pq.poll();//넣었던 값들 중 맨 앞에 있는 값을 꺼낸다(PriorityQueue)
            
            //여기서 이제 제일 중요
            //없으면 시간초과남
            //만약 이미 방문한 정점이면 그냥 밑에 반복들을 다 하지말자!
            //그래서 방문 안 한 노드만 검사하기 위해 그냥 continue때려 박아버리기
            if(visit[now.v]) continue;
            int End=now.v;//지금 들어온 정점의 번호           
           
            //int W=now.w;//지금 들어온 정점의 가중치 값 이 가중치 값은 지금까지 들어온 가중치 값의 최소값이다.
            visit[End]=true;
            //visit[end]=true;//입력되는 정점들은 다시 안 올 것이기 때문에 true로 바꿔 방문처리
            //지금 들어온 정점과 연결된 정점의 수 만큼 반복
            	for (int i = 0; i <list[End].size(); i++) {            	
                Node cur=list[End].get(i);//연결된 수 중 가중치 값이 가장 작은 정점의 번호와 가중치 값을 cur값에 저장
                int END=cur.v;//이제 now의 Node와 연결된 cur Node의 정점을 END에 저장
                int ww=cur.w;//역시  now의 Node와 연결된 cur Node의 가중치 값을 ww에 저장
                //만약 이제 확인하려는 정점이 이미 방문한 배열이 아니고
                //지금까지 저장된 최소값의 가중치와 지금 이어진 상태의 가중치의 합이 저장되어 있는
                //새로운 정점의 가중치 값보다 크다면
                if (!visit[END]&&arr[END]>arr[End]+ww) {                	
                	//그 값을 더 작은 값으로 초기화 해준다.
                    arr[END]=arr[End]+ww;
                    //그리고 그 노드의 끝점과 그 끝점의 최소 가중치 값을 큐에 다시 넣어준다.
                    pq.add(new Node(END,arr[END]));
                }
            }
        }
    }
}

노드의 끝값과 가중치 값을 받을 클래스를 하나 만들고 인접 리스트로 받기 위한 반복문을 해주고 역시 boolean의 방문 배열도 만들어줘야한다.

다익스트라 메서드에  priorityQueue를 선언해준다.

그리고 바로 그 시작점과 0 값을 큐에 넣고 while 반복문이 비지 않을 때까지 큐에서 가장 앞에 있는 값을 빼고 만약 뺀 값이 방문했던 정점이라면 continue를 하면서 시간을 줄인다. 이거 안해주면 바로 시간 초과 남. continue하면서 시간을 절약해주자.

확인할 노드의 정점을 저장하고 그 정점을 방문표시해준다.

그리고 확인할 정점과 연결되어 있는 정점들을 확인해줄 반복문을 연결된 정점의 수만큼 for문을 작성한다.

그 반복문 안에는 연결된 정점의 번호와 거기까지의 가중치 값이고 만약 그 정점이 방문하지 않은 정점이고 지금까지의 가중치 값의 최소값과 연결된 가중치의 합이 연결된 정점의 가중치값의 최소값보다 작다면 지금까지의 가중치 값과 연결된 가중치의 합으로 바꿔준다.

그리고 그 정점과 그 정점의 가중치 값을 큐에 바로 넣어준다.