ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [백준] 2307번 도로검문
    문제 풀이 2020. 7. 26. 17:26

    2307 도로검문


    문제


    그림 1은 어떤 도시의 주요 지점과 그 지점들 간의 이동시간을 나타낸 그래프이다. 그래프의 노드는 주요 지점을 나타내고  두 지점을 연결한 도로(에지)에 표시된 수는 그 도로로 이동할 때 걸리는 분 단위의 시간을 나타낸다. 두 지점 a와 b를 연결하는 도로는 도로(a,b)로 표시한다.

    예를 들어 도로(1,2)와 도로(2,3)를 통하여 지점1에서 지점3으로 갈 때 걸리는 시간은 3분이다. 도로는 모두 양방향이라고 가정하므로 도로(a,b)와 도로(b,a)를 지날 때 걸리는 시간은 항상 같다고 한다.

    오세준은 입력 데이터에 표시된 도시로 진입하여 이 도시를 가장 빠른 시간 내에 빠져나가고자 한다. 그런데 이 사실을 알고 있는 류원룡이 어떤 하나의 도로(에지)를 선택하여 이 도로에서 검문을 하려고 한다. 따라서 오세준은 이 도로를 피해서 가장 빠르게 도시를 탈출하고자 한다. 이 경우 류원룡이 검문을 위하여 선택하는 도로에 따라서 오세준의 가장 빠른 탈출시간은 검문이 없을 때에 비하여 더 늘어날 수 있다.

    문제는 도로검문을 통하여 얻을 수 있는 탈출의 최대 지연시간을 계산하는 것이다. 추가설명은 다음과 같다

    1.두 개의 지점을 직접 연결하는 도로가 있는 경우, 그 도로는 유일하다. 

    2. 도시의 지점(노드)은 1에서 N번까지  N개의 연속된 정수로 표시된다.

    3. 오세준이 도시에 진입하는 지점은  항상 1번이고 도시를 빠져 나가기 위하여 최종적으로 도달해야하는 지점은 항상 N번 지점이다.

    4. 오세준은 검문을 피해서 가장  빨리 도시를 빠져나가고자 하고, 류원룡은 적절한 도로를 선택하여 이 오세준들의 탈출시간을 최대한 지연시키고자 한다.

    5. 각 도시 지점 간을 이동하는 시간은 항상 양의 정수이다.

    입력 도시의 도로망에 따라서 류원룡이 어떤 도로를 막으면 오세준은 도시를 탈출하지 못할 수도 있다. 이 경우 검문으로 인하여 지연시킬 수 있는 탈출시간은 무한대이다. 이 경우에는 -1을 출력해야 한다.

    그림 1에서 볼 때 검문이 없을 경우 오세준이 도시를 탈출하는데 걸리는 시간은 4분이다. 만일 류원룡이 도로(4,3)을 막으면 그 탈출시간을 지연시킬 수 없으므로 지연시간은 0이다. 만일 도로(2,3)을 막으면, 오세준들이 가장 빠르게 탈출할 수 있는 시간은 5분이므로 탈출지연시간은 1분이고, 도로(3,6)을 막으면 탈출지연시간은 2분이다.

    여러분은 입력 데이터에 표시된 도로망을 읽고, 류원룡이 한 도로를 막고 검문함으로써 지연시킬 수 있는 최대시간을 정수로 출력하여야한다. 만일 지연효과가 없으면 0을 출력해야하고, 도시를 빠져나가지 못하게 만들 수 있으면(지연시간이 무한대) -1을 출력해야 한다.


    입력


    첫 줄에는 지점의 수를 나타내는 정수  N(6 ≤ N ≤ 1000)과 도로의 수 M(6 ≤ M ≤ 5000)이 주어진다. 그 다음 이어 나오는 M개의 각 줄에는 도로(a, b)와 그 통과시간 t가 a b t 로 표시된다. 단 이 경우 a < b 이고 1 ≤ t ≤ 10000이다.


    출력


    경찰이 하나의 도로를 막음으로써 지연시킬 수 있는 최대 시간을 정수로 출력한다. (단, 그 지연시간이 무한대이면 -1을 출력해야 한다.)



    풀이)

    다익스트라를 이용해서 푼 문제이다.


    핵심은 도로를 막는 방법이다.

    M개의 도로를 하나씩 제외하면서, 지연 시간을 M번이나 구할 경우 시간 초과가 발생한다

    시간복잡도가 O(M(NM)logN) 이기 때문이다.


    M개의 도로 일일히 제외해보는 것이 아니라

    처음 아무 도로를 막지 않았을 때 구한 최단 경로 상의 도로를 하나씩 제외해봐야 한다.

    이 경로 상의 도로를 제외하는 게 아니라면, 오세준은 최단 경로를 이용해서 지연시간이 생기지 않기 때문이다.


    즉, 처음에 최단 시간을 만드는 경로를 구한 후

    그 경로를 이루는 도로 각각을 제외해보며 지연시간을 구하면 된다.


    입력예시로 보자면


    최단 시간 : 4분

    최단 경로 : 1 -> 2 -> 3 ->6


    최단 경로를 이루는 3-6 사이의 도로, 2-3 사이의 도로, 1-2 사이의 도로를 한 번씩 막은 뒤 

    각각 1->N까지 가는 시간을 구한 후 그 시간이 맨 처음 4초에 비해 얼마나 지연됬는지를 보면 된다.


    3-6 도로를 막을 경우 : 걸리는 시간 6분 => 지연시간 6-4 = 2분

    2-3 도로를 막을 경우 : 걸리는 시간 5분 => 지연시간 5-4 = 1분

    1-2 도로를 막을 경우 : 걸리는 시간 5분 => 지연시간 5-4 = 1분


    따라서 최대 지연시간은 2분



    코드)


    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    #include <stdio.h>
    #include <vector>
    #include <queue>
    #include <algorithm>
    using namespace std;
     
    #define INF 999999999
    int N, M;
    vector <pair<intint>> adj[1001];
    vector <int> parent; // N까지 가는 경로 저장
    int b_city1 = 0, b_city2 = 0;//막는 도로를 이루는 도시 2개
    bool first = true;//아무 도로도 막지 않았을 때의 최단 경로 구하기 위해 
     
    //도시 a와 b가 막힌 도로로 이루어진지 확인
    //막힌 도로라면 true return
    bool unlink(int a, int b) {
        if (b_city1 == a && b_city2 == b)return true;
        else if (b_city1 == b && b_city2 == a) return true;
        return false;
    }
     
    int dijkstra() {
        priority_queue <pair<intint>> qu;
        vector <int> dist(N + 1, INF);
        dist[1= 0;
        qu.push({ 0,1 });
        while (!qu.empty())
        {
            int cost = -qu.top().first;
            int here = qu.top().second;
            qu.pop();
            if (cost > dist[here]) continue;
            for (int i = 0; i < adj[here].size(); i++) {
                int next = adj[here][i].first;
                if (unlink(here,next)) continue;//도로가 막힘
                int next_cost = adj[here][i].second + cost;
                if (next_cost < dist[next]) {
                    if(first) parent[next] = here; //맨 처음 최단 시간을 구하는 dijstra일 경우 경로 저장
                    dist[next] = next_cost;
                    qu.push({ -next_cost,next });
                }
            }
        }
        return dist[N];
    }
    int main() {
        //freopen("input.txt", "r", stdin);
        scanf("%d %d"&N, &M);
        parent.resize(N + 1-1);
        for (int i = 0; i < M; i++) {
            int a, b, t;
            scanf("%d %d %d"&a, &b, &t);
            adj[a].push_back({ b,t });
            adj[b].push_back({ a,t });
        }
        //1. 1번에서 N지점까지 가는 최단 시간측정, 경로 저장 
        parent[1= 1;
        int time = dijkstra();
        first = false;
     
        //2. 최단 경로를 이루는 도로 하나씩 막아보고, 시간 측정 
        int max_time = 0;
        //최선 경로상의 도로 지움 -> N부터 거꾸로 도로 하나씩 지움
        for (int p = N; p != parent[p]; p = parent[p]) {
            b_city1 = p;
            b_city2 = parent[p];
            max_time = max(max_time, dijkstra());
            if (max_time == INF) break;
        }
        if (max_time == INF) printf("-1");
        else printf("%d", max_time - time);
     
        return 0;
    }
    cs


    '문제 풀이' 카테고리의 다른 글

    [백준] 5213번 과외맨  (0) 2020.07.28
    [백준] 1504번 특정한 최단 경로  (0) 2020.07.27
    [백준] 9376번 탈옥  (1) 2020.07.25
    [백준] 1655번 가운데를 말해요  (0) 2020.07.24
    [백준] 1854번 k번째 최단 경로 찾기  (0) 2020.07.23

    댓글

Designed by Tistory.