[백준] 9370. 미확인 도착지

6 분 소요

문제 링크

[백준] 9370. 미확인 도착지


풀이 과정

풀이 1

다익스트라 알고리즘을 적용해 풀 수 있는 문제입니다. 시작 정점으로부터 목적지 후보 정점에 도착했을 때, 최단 거리 경로에 g-h 간선이 포함되는 지 판단해야 합니다.

문제를 풀며 주의해야 할 조건은 다음과 같습니다.

  1. g-h 간선의 사용 유무를 저장해 두어야 합니다.
  2. 정점까지 같은 비용으로 가더라도, g-h 간선을 포함해 가는 경로가 있다면, 해당 정점까지는 g-h 간선을 이용해 간 것으로 설정해 줘야 합니다.


private static class Edge {
    int vertex, cost;
}

private static class Info {
    int dist;
    boolean used;
}

위 코드와 같이 간선 정보를 저장하는 클래스 Edge와 별개로, 간선에 대한 최단 거리( dist ) 및 g-h 간선의 사용 유무( used )를 체크하기 위한 Info 클래스를 하나 두었습니다.


또한, 정점까지 도달하는 데에 드는 비용이 같다면, 이전 경로는 무시하고 g-h 간선을 사용하도록 해주어야 합니다.

위 예시처럼, 같은 비용의 다른 경로가 존재하고, 그 경로(파란색 경로)가 g-h 간선을 포함한다면, g-h 간선을 이용해 간 것으로 판단해줘야 합니다. 다시 말해, g-h 간선을 이용해서 도달이 가능하다고 판단해 주어야 합니다.


for (Edge next : adj[now.vertex]) {
    if (!visit[next.vertex] && infos[next.vertex].dist >= infos[now.vertex].dist + next.cost) {
        if(infos[next.vertex].used && infos[next.vertex].dist == infos[now.vertex].dist + next.cost) continue;

        infos[next.vertex].dist = infos[now.vertex].dist + next.cost;
        infos[next.vertex].used = infos[now.vertex].used;

        Edge edge = new Edge(next.vertex, infos[next.vertex].dist);

        pq.add(edge);

        if ((now.vertex == g && next.vertex == h) || (now.vertex == h && next.vertex == g)) {
            infos[next.vertex].used = true;
        }
    }
}

아직 방문하지 않은 인접한 정점에 대해 아래와 같은 과정을 거칩니다.

  1. 다음 정점에 대해, 이미 저장되어 있는 최소 비용과 현재 정점까지의 최소 비용에 다음 정점까지의 비용을 더했을 때의 비용같고, g-h 간선을 사용한 이력이 있으면 갱신 및 큐에 삽입하는 과정은 생략합니다.
  2. 이미 저장되어 있는 최소 비용보다 현재 정점까지의 최소 비용에 다음 정점까지의 비용을 더했을 때의 비용작다면, 갱신시켜 줍니다. 이 때, 현재 정점과 다음 정점이 g, h 쌍이라면 g-h 간선의 사용 유무를 체크(used 를 true 로) 해줍니다.

풀이 2

출발지로부터 목적지 후보까지 간선 g-h 를 포함하는 경로는 다음과 같이 두 가지로 구분됩니다.

출발지 → g → h → 목적지 후보
출발지 → h → g → 목적지 후보

또한, 최단 거리로 이동하므로, 각 부분 경로들도 최단 비용을 만족해야 합니다. 다시 말하면, 출발지로부터 목적지 후보까지의 최단 거리는 아래 두 경우 중 하나 이상 만족해야만 간선 g-h를 이용했다고 판단할 수 있습니다. 아니고서는 g-h 간선을 이용하지 않은 경로로 간주할 수 있습니다.

출발지 → 목적지 후보 최단 거리
= (출발지 → g 최단 거리) + (g-h 간선 비용) +( h → 목적지 후보 최단 거리)
OR (출발지 → h 최단 거리) + (h-g 간선 비용) + (g → 목적지 후보 최단 거리)

해당 풀이는 각 정점 s, g, h 에 대해서 다익스트라를 각각 수행(총 세 번)합니다. 이렇게 얻어진 출발지를 다르게 돌렸을 때 각 정점에 대한 최단거리 역시 서로 다른 배열( dist_s , dist_g , dist_h ) 에 저장하고 갱신합니다.


if (dist_s[idx] == dist_s[g] + dist_g[h] + dist_h[idx]
        || dist_s[idx] == dist_s[h] + dist_h[g] + dist_g[idx]) {
    candidate.put(idx, true);
}

위 코드는 간선 g-h를 사용했나 판단하는 과정입니다. 위에서 적은 것처럼, dist_s[idx] == dist_s[g] + dist_g[h] + dist_h[idx] 는 최단거리로 먼저 g를 방문한 후 이후 h, 목적지 순으로 비용을 더해 해당 비용이 최소 비용을 만족하는 지, dist_s[idx] == dist_s[h] + dist_h[g] + dist_g[idx] 는 최단거리로 먼저 h를 방문한 이후 g, 목적지 순으로 비용을 더해 해당 비용이 최소 비용을 만족하는 지를 판별합니다. 두 경우 중 하나만 만족하더라도 간선 g-h를 사용해 최단 비용으로 목적지 후보에 도달했다는 결론을 내릴 수 있습니다.


코드

풀이 1

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
    static int n, m, t, s, g, h;
    static ArrayList<Edge>[] adj;
    static Info[] infos;
    static boolean[] visit;
    static TreeMap<Integer, Boolean> candidate;

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

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

        for (int tc = 1; tc <= TC; tc++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            n = Integer.parseInt(st.nextToken());
            m = Integer.parseInt(st.nextToken());
            t = Integer.parseInt(st.nextToken());

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

            adj = new ArrayList[n + 1];
            for (int i = 1; i <= n; i++)
                adj[i] = new ArrayList<>();

            infos = new Info[n + 1];
            for (int i = 1; i <= n; i++)
                infos[i] = new Info(Integer.MAX_VALUE, false);

            visit = new boolean[n + 1];

            for (int i = 0; i < m; i++) {
                st = new StringTokenizer(br.readLine());
                int v1, v2, cost;

                v1 = Integer.parseInt(st.nextToken());
                v2 = Integer.parseInt(st.nextToken());
                cost = Integer.parseInt(st.nextToken());

                adj[v1].add(new Edge(v2, cost));
                adj[v2].add(new Edge(v1, cost));
            }

            candidate = new TreeMap<>();
            for (int i = 0; i < t; i++)
                candidate.put(Integer.parseInt(br.readLine()), false);

            PriorityQueue<Edge> pq = new PriorityQueue<>(new Comparator<Edge>() {
                @Override
                public int compare(Edge o1, Edge o2) {
                    return o1.cost - o2.cost;
                }
            });

            infos[s].dist = 0;
            Edge start = new Edge(s, 0);
            pq.add(start);

            while (!pq.isEmpty()) {
                Edge now = pq.poll();
                visit[now.vertex] = true;

                if (candidate.containsKey(now.vertex) && infos[now.vertex].used) {
                    candidate.put(now.vertex, true);
                }

                for (Edge next : adj[now.vertex]) {
                    if (!visit[next.vertex] && infos[next.vertex].dist >= infos[now.vertex].dist + next.cost) {
                        if(infos[next.vertex].used && infos[next.vertex].dist == infos[now.vertex].dist + next.cost) continue;

                        infos[next.vertex].dist = infos[now.vertex].dist + next.cost;
                        infos[next.vertex].used = infos[now.vertex].used;

                        Edge edge = new Edge(next.vertex, infos[next.vertex].dist);

                        pq.add(edge);

                        if ((now.vertex == g && next.vertex == h) || (now.vertex == h && next.vertex == g)) {
                            infos[next.vertex].used = true;
                        }
                    }
                }
            }

            for (Map.Entry entry : candidate.entrySet()) {
                if ((boolean) entry.getValue()) {
                    System.out.print(entry.getKey() + " ");
                }
            }
            System.out.println();
        }
    }

    private static class Edge {
        int vertex, cost;

        public Edge(int vertex, int cost) {
            this.vertex = vertex;
            this.cost = cost;
        }
    }

    private static class Info {
        int dist;
        boolean used;

        public Info(int dist, boolean used) {
            this.dist = dist;
            this.used = used;
        }
    }
}

풀이 2

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
    static int n, m, t, s, g, h;
    static ArrayList<Edge>[] adj;
    static int[] dist_s, dist_g, dist_h;
    static boolean[] visit;
    static TreeMap<Integer, Boolean> candidate;

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

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

        for (int tc = 1; tc <= TC; tc++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            n = Integer.parseInt(st.nextToken());
            m = Integer.parseInt(st.nextToken());
            t = Integer.parseInt(st.nextToken());

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

            adj = new ArrayList[n + 1];
            for (int i = 1; i <= n; i++)
                adj[i] = new ArrayList<>();

            for (int i = 0; i < m; i++) {
                st = new StringTokenizer(br.readLine());
                int v1, v2, cost;

                v1 = Integer.parseInt(st.nextToken());
                v2 = Integer.parseInt(st.nextToken());
                cost = Integer.parseInt(st.nextToken());

                adj[v1].add(new Edge(v2, cost));
                adj[v2].add(new Edge(v1, cost));
            }

            candidate = new TreeMap<>();
            for (int i = 0; i < t; i++)
                candidate.put(Integer.parseInt(br.readLine()), false);

            dist_s = new int[n + 1];
            dist_g = new int[n + 1];
            dist_h = new int[n + 1];

            Arrays.fill(dist_s, Integer.MAX_VALUE);
            Arrays.fill(dist_g, Integer.MAX_VALUE);
            Arrays.fill(dist_h, Integer.MAX_VALUE);

            dijkstra(s, dist_s);
            dijkstra(g, dist_g);
            dijkstra(h, dist_h);

            for (Map.Entry entry : candidate.entrySet()) {
                int idx = (int) entry.getKey();

                if (dist_s[idx] == dist_s[g] + dist_g[h] + dist_h[idx]
                        || dist_s[idx] == dist_s[h] + dist_h[g] + dist_g[idx]) {
                    candidate.put(idx, true);
                }
            }

            for (Map.Entry entry : candidate.entrySet()) {
                if ((boolean) entry.getValue()) {
                    System.out.print(entry.getKey() + " ");
                }
            }
            System.out.println();
        }
    }

    private static void dijkstra(int start, int[] dist) {
        PriorityQueue<Edge> pq = new PriorityQueue<>(new Comparator<Edge>() {
            @Override
            public int compare(Edge o1, Edge o2) {
                return o1.cost - o2.cost;
            }
        });
        visit = new boolean[n + 1];
        dist[start] = 0;
        pq.add(new Edge(start, 0));

        while (!pq.isEmpty()) {
            Edge now = pq.poll();
            visit[now.vertex] = true;

            for (Edge next : adj[now.vertex]) {
                if (!visit[next.vertex] && dist[next.vertex] > dist[now.vertex] + next.cost) {
                    dist[next.vertex] = dist[now.vertex] + next.cost;
                    pq.add(new Edge(next.vertex, dist[next.vertex]));
                }
            }
        }
    }

    private static class Edge {
        int vertex, cost;

        public Edge(int vertex, int cost) {
            this.vertex = vertex;
            this.cost = cost;
        }
    }
}

카테고리:

업데이트:

댓글남기기