반응형
05-15 00:00
Today
Total
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
관리 메뉴

개발하는 고라니

[백준] 9370번 : 미확인 도착지 본문

Programming/백준

[백준] 9370번 : 미확인 도착지

조용한고라니 2021. 2. 2. 01:54
반응형
 

9370번: 미확인 도착지

(취익)B100 요원, 요란한 옷차림을 한 서커스 예술가 한 쌍이 한 도시의 거리들을 이동하고 있다. 너의 임무는 그들이 어디로 가고 있는지 알아내는 것이다. 우리가 알아낸 것은 그들이 s지점에서

www.acmicpc.net

# 문제

(취익)B100 요원, 요란한 옷차림을 한 서커스 예술가 한 쌍이 한 도시의 거리들을 이동하고 있다. 너의 임무는 그들이 어디로 가고 있는지 알아내는 것이다. 우리가 알아낸 것은 그들이 s지점에서 출발했다는 것, 그리고 목적지 후보들 중 하나가 그들의 목적지라는 것이다. 그들이 급한 상황이기 때문에 목적지까지 우회하지 않고 최단거리로 갈 것이라 확신한다. 이상이다. (취익)

어휴! (요란한 옷차림을 했을지도 모를) 듀오가 어디에도 보이지 않는다. 다행히도 당신은 후각이 개만큼 뛰어나다. 이 후각으로 그들이 g와 h 교차로 사이에 있는 도로를 지나갔다는 것을 알아냈다.

이 듀오는 대체 어디로 가고 있는 것일까?

예제 입력의 두 번째 케이스를 시각화한 것이다. 이 듀오는 회색 원에서 두 검은 원 중 하나로 가고 있고 점선으로 표시된 도로에서 냄새를 맡았다. 따라서 그들은 6으로 향하고 있다.

# 입력

첫 번째 줄에는 테스트 케이스의 T(1 ≤ T ≤ 100)가 주어진다. 각 테스트 케이스마다

  • 첫 번째 줄에 3개의 정수 n, m, t (2 ≤ n ≤ 2 000, 1 ≤ m ≤ 50 000 and 1 ≤ t ≤ 100)가 주어진다. 각각 교차로, 도로, 목적지 후보의 개수이다.
  • 두 번째 줄에 3개의 정수 s, g, h (1 ≤ s, g, h ≤ n)가 주어진다. s는 예술가들의 출발지이고, g, h는 문제 설명에 나와 있다. (g ≠ h)
  • 그 다음 m개의 각 줄마다 3개의 정수 a, b, d (1 ≤ a < b ≤ n and 1 ≤ d ≤ 1 000)가 주어진다. a와 b 사이에 길이 d의 양방향 도로가 있다는 뜻이다.
  • 그 다음 t개의 각 줄마다 정수 x가 주어지는데, t개의 목적지 후보들을 의미한다. 이 t개의 지점들은 서로 다른 위치이며 모두 s와 같지 않다.

교차로 사이에는 도로가 많아봐야 1개이다. m개의 줄 중에서 g와 h 사이의 도로를 나타낸 것이 존재한다. 또한 이 도로는 목적지 후보들 중 적어도 1개로 향하는 최단 경로의 일부이다.

# 출력

테스트 케이스마다

  • 입력에서 주어진 목적지 후보들 중 불가능한 경우들을 제외한 목적지들을 공백으로 분리시킨 오름차순의 정수들로 출력한다.

[Dijkstra 알고리즘]

개인적으로 난이도는 어렵지 않으나 코드가 길어지는 문제라고 생각된다. 문제를 이해하는 것이 조금 난해했다고 해야하나... 한 4번 째 읽는 것에서 완벽히 이해했다. 각 테스트 케이스에서 정점의 개수(n), 간선의 개수(m), 목적지 후보의 개수(t)가 주어진다.

 

그 다음 줄에서 s, g, h가 주어지는데 s는 시작 정점, g, h는 반드시 지나가야하는 간선이라고 생각하면 된다. 문제 풀이 시 주의할 점은, 시작 정점 s에서 g와 h 중 더 가까운(최단 경로가 더 작은 값) 곳을 구해야 한다. 문제 풀이의 핵심은 

 

1) 시작 정점에서 목적지 후보 정점까지의 최단 경로

2) 시작 정점 -> g -> g와 h사이의 가중치 -> h 에서 목적지 후보 정점 까지의 최단 경로 (시작 정점에서 g까지의 거리가 더 짧을 때로 가정)

 

를 비교해 같으면 답으로 출력해야하고, 다르면 출력해선 안된다.

 

1) 을 구하는 것은 일반적인 다익스트라 알고리즘으로 금방 구할 수 있으니 distance[0][]에 구해놨다고 치고,

 

2) 를 구하기 위해 distance[0][g]와 distance[0][h]를 비교해 더 적은 쪽을 택한다. 이 값을 기억해놓자.

h정점과 g정점 사이 간선의 가중치(bridge)는 인접 리스트를 돌며 구해놓을 수 있다.

            int bridge = -1;
            for (Edge e:list[g]) //인접 리스트 recursion
                if(e.tg == h) bridge = e.d; //g와 h 사이 간선의 가중치 get

이제 distance[1][]에 h 에서의 최단 경로를 구한다. 그럼 준비물은 모두 챙긴 것이다. 이제 1)과 2)를 비교해주기만 하면 된다. (min에 더 작은 값 g가 저장되어 있다.)

            Arrays.sort(arrive); //오름차순 정렬 하는 것이 좋다.
            
            for(int idx:arrive)
                if(distance[0][idx] == (distance[0][min] + bridge + distance[1][idx]))
                    System.out.print(idx + " ");
            System.out.println();

# Code </>

public class Main {

    static class Edge implements Comparable<Edge>{
        int tg, d;
        public Edge(int target, int dist){
            tg = target; d = dist;
        }
        @Override
        public int compareTo(Edge o) {
            return d-o.d;
        }
    }

    static List<Edge>[] list;
    static Queue<Edge> Q = new PriorityQueue<>();
    static final int INF = 1000000000;
    static int[] distance[], arrive;
    static int v, e;

    static void Dijkstra(int start, int idx) {
        
        distance[idx][start] = 0;
        Q.add(new Edge(start, 0));

        while(!Q.isEmpty()){
            Edge edge = Q.poll();
            int current = edge.tg;
            int d = edge.d;

            if(distance[idx][current] < d) continue;

            for(Edge e : list[current]){
                int next = e.tg;
                int nextD = d + e.d;

                if(distance[idx][next] > nextD){
                    distance[idx][next] = nextD;
                    Q.add(new Edge(next, nextD));
                }
            }
        }
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;

        int t = Integer.parseInt(br.readLine());

        for(int a=0; a<t; a++) {

            st = new StringTokenizer(br.readLine());
            v = Integer.parseInt(st.nextToken());
            e = Integer.parseInt(st.nextToken());
            int arrival = Integer.parseInt(st.nextToken());

            st = new StringTokenizer(br.readLine());
            int start = Integer.parseInt(st.nextToken());
            int g = Integer.parseInt(st.nextToken());
            int h = Integer.parseInt(st.nextToken());

            distance = new int[2][v+1];
            arrive = new int[arrival];
            list = new ArrayList[v+1];
            for(int i=1; i<=v;i++)
                list[i] = new ArrayList<>();

            for(int i=0; i<2; i++)
                for(int j=1; j<=v; j++)
                    distance[i][j] = INF;

            for(int i=0; i<e; i++){
                st = new StringTokenizer(br.readLine());
                int b = Integer.parseInt(st.nextToken());
                int c = Integer.parseInt(st.nextToken());
                int d = Integer.parseInt(st.nextToken());

                list[b].add(new Edge(c, d));
                list[c].add(new Edge(b, d));
            }

            for(int i=0; i<arrival; i++)
                arrive[i] = Integer.parseInt(br.readLine());

            int min = INF;
            int bridge = -1;
            for (Edge e:list[g])
                if(e.tg == h) bridge = e.d;

            Dijkstra(start, 0);

            if(distance[0][g] < distance[0][h]){
                min = g;
                Dijkstra(h, 1);
            }

            else{
                min = h;
                Dijkstra(g, 1);
            }

            Arrays.sort(arrive);
            for(int idx:arrive)
                if(distance[0][idx] == (distance[0][min] + bridge + distance[1][idx]))
                    System.out.print(idx + " ");
            System.out.println();

            /*System.out.println("# 시작 정점에서의 최단 거리");
            Arrays.stream(distance[0]).skip(1).forEach(i->System.out.print(i + " "));
            System.out.println();
            System.out.println("# 중간 정점에서의 최단 거리");
            Arrays.stream(distance[1]).skip(1).forEach(i->System.out.print(i + " "));
            System.out.println();
            System.out.println("# Bridge: " + bridge);*/
        }
    }
}
반응형

'Programming > 백준' 카테고리의 다른 글

[백준] 2583번 : 영역 구하기  (0) 2021.02.02
[백준] 2468번 : 안전 영역  (0) 2021.02.02
[백준] 1956번 : 운동  (0) 2021.02.02
[백준] 17472번 : 다리 만들기 2  (0) 2021.01.31
[백준] 2276번 : 물 채우기  (0) 2021.01.30
Comments