궤도

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

💻 현생/⛓ 알고리즘

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

영이오 2021. 4. 26. 21:24

문제

 


풀이

 

굳이 안그래도 될걸 억지로 어렵게 만들었다는 느낌이 드는 문제다.

 

myunji.tistory.com/345

 

[백준] 1504번 : 특정한 최단 경로

문제 풀이 반드시 거쳐야 하는 정점이 B, C라고 하자. 이 둘을 거친 최단 경로는 1->B->C->N 또는 1->C->B->N일 것이다. 이걸 구하기 위해선 다익스트라 알고리즘을 3번 실행해야 한다. 1에 대하여 실행

myunji.tistory.com

논리는 이 문제랑 같다.

 

일단 s->t 최단 거리를 구한다. 만약 s->g->h->t 또는 s->h->g->t 의 최단거리와 s->t와 같다면 목적지가 될 수 있다.


소스코드

 

#include <iostream>
#include <vector>
#include <queue>
#include <utility>
#include <string>
#include <algorithm>

using namespace std;

const int INF = 1e9 * 2;
typedef pair<int, int> pp;

vector<vector<pp>> graph; //graph[i][j].first = dest, graph[i][j].second = weight
vector<int> V_dist; //INF로 초기화
vector<bool> visited; //방문 체크
priority_queue<pp, vector<pp>, greater<pp>> min_dist; //min-heap, min_dist.first = distance, min_dist.second = source
vector<int> result;

void init(int size) { //초기화
    V_dist.assign(size, INF);
    visited.assign(size, false);
}

void dijkstra(int start) {
    int new_dist;

    V_dist[start] = 0; //시작점의 거리는 0
    min_dist.push(make_pair(V_dist[start], start)); //거리와 source 투입

    while (!min_dist.empty()) { //우선순위 큐가 비어있지 않을 때
        int dist = min_dist.top().first; //현재 노드 까지의 최단 거리
        int source = min_dist.top().second; //노드
        min_dist.pop();

        if (!visited[source]) { //방문한 적 없는 노드라면
            for (int i = 0; i < graph[source].size(); i++) { //해당 노드가 source인 모든 정점(dest)
                new_dist = dist + graph[source][i].second; //현재 노드까지의 최단거리 + source에서 dest까지의 weight
                if (new_dist < V_dist[graph[source][i].first]) { //이 값이 기존의 V_dist[dest]보다 작다면
                    V_dist[graph[source][i].first] = new_dist; //dest의 최단거리 갱신
                    min_dist.push(make_pair(new_dist, graph[source][i].first)); //우선순위 큐에 거리와 dest 노드 정보 삽입
                }
            }
        }
        visited[source] = true; //source 노드 방문 처리
    }
}

void localDijkstra(int start, int size, int weight, vector<int> candi, vector<int> dist) { //g, h에서 다익스트라 후 경유 확인
    init(size);
    dijkstra(start);
    for (int j = 0; j < candi.size(); j++) {
        if ((weight + V_dist[candi[j]]) == dist[j])
            result.push_back(candi[j]);
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    int T, n, m, t, s, g, h, a, b, d, x, gh_length, to_g, to_h;

    cin >> T;
    for (int i = 0; i < T; i++) {
        vector<int> dest_candi, dest_dist;

        cin >> n >> m >> t >> s >> g >> h;
        graph.assign(n + 1, vector<pp>(0));
        for (int j = 0; j < m; j++) {
            cin >> a >> b >> d;
            if ((a == g && b == h) || (a == h && b == g)) //gh 사이의 거리
                gh_length = d;
            graph[a].push_back(make_pair(b, d));
            graph[b].push_back(make_pair(a, d));
        }
        for (int j = 0; j < t; j++) {
            cin >> x;
            dest_candi.push_back(x);
        }
        sort(dest_candi.begin(), dest_candi.end()); //도착지점 후보들 정렬

        init(n + 1);
        dijkstra(s); //s를 시작점으로 한 다익스트라
        for (int j = 0; j < t; j++) //각 도착지점 후보들까지의 최단거리 저장
            dest_dist.push_back(V_dist[dest_candi[j]]);
        to_g = V_dist[g]; //s->g 까지의 거리
        to_h = V_dist[h]; //s->h 까지의 거리

        localDijkstra(g, n + 1, to_h + gh_length, dest_candi, dest_dist); //s->h->g->t가 t까지의 최단거리와 동일한지 확인
        localDijkstra(h, n + 1, to_g + gh_length, dest_candi, dest_dist); //s->g->h->t가 t까지의 최단거리와 동일한지 확인

        sort(result.begin(), result.end()); //정렬
        result.erase(unique(result.begin(), result.end()), result.end()); //중복제거
        for (int j = 0; j < result.size(); j++) //출력
            cout << result[j] << ' ';
        cout << '\n';
        result.clear(); //초기화
    }
}

전체 소스코드다. 다익스트라 부분은 설명하지 않겠다.

 

        for (int j = 0; j < m; j++) {
            cin >> a >> b >> d;
            if ((a == g && b == h) || (a == h && b == g)) //gh 사이의 거리
                gh_length = d;
            graph[a].push_back(make_pair(b, d));
            graph[b].push_back(make_pair(a, d));
        }

그래프 벡터에 간선 정보를 넣으면서 g-h 간선이 있는지 확인한다. 찾으면 gh_length에 저장한다.

g와 h 사이의 거리라는 뜻이다.

 

        for (int j = 0; j < t; j++) {
            cin >> x;
            dest_candi.push_back(x);
        }
        sort(dest_candi.begin(), dest_candi.end()); //도착지점 후보들 정렬

도착지점 후보들을 입력받고 오름차순으로 정렬한다.

 

        init(n + 1);
        dijkstra(s); //s를 시작점으로 한 다익스트라
        for (int j = 0; j < t; j++) //각 도착지점 후보들까지의 최단거리 저장
            dest_dist.push_back(V_dist[dest_candi[j]]);
        to_g = V_dist[g]; //s->g 까지의 거리
        to_h = V_dist[h]; //s->h 까지의 거리

시작점에 대해 다익스트라 알고리즘을 실행한다.

그리고 각 도착지점 후보들까지의 최단 거리를 dest_dist 벡터에 저장하고, g, h까지의 거리도 저장한다.

 

void init(int size) { //초기화
    V_dist.assign(size, INF);
    visited.assign(size, false);
}

init 함수는 그냥 초기화 함수다.

 

        localDijkstra(g, n + 1, to_h + gh_length, dest_candi, dest_dist); //s->h->g->t가 t까지의 최단거리와 동일한지 확인
        localDijkstra(h, n + 1, to_g + gh_length, dest_candi, dest_dist); //s->g->h->t가 t까지의 최단거리와 동일한지 확인

그리고 g, h에 대한 localDijkstra 함수를 실행해 가능한 도착지점을 체크한다.

 

void localDijkstra(int start, int size, int weight, vector<int> candi, vector<int> dist) { //g, h에서 다익스트라 후 경유 확인
    init(size);
    dijkstra(start);
    for (int j = 0; j < candi.size(); j++) {
        if ((weight + V_dist[candi[j]]) == dist[j])
            result.push_back(candi[j]);
    }
}

start=g일 때를 예로 들자면

g를 시작점으로 하는 다익스트라를 실행하고 도착지점 후보 정점 t에 대해

s->h->g->t와 s->t의 거리가 같은지 확인한다.

 

s->h와 h->g는 각각 to_h와 gh_length로 둘을 weight 변수로 합해 함수에 전달했다.

그래서 g->t인 V_dist[t]+weight가 dist[t](s->t)와 같다면 result에 t를 넣어준다.

 

        sort(result.begin(), result.end()); //정렬
        result.erase(unique(result.begin(), result.end()), result.end()); //중복제거
        for (int j = 0; j < result.size(); j++) //출력
            cout << result[j] << ' ';
        cout << '\n';
        result.clear(); //초기화

result 벡터는 정렬 후 중복을 제거하고, 다음 테스트 케이스를 위해 초기화 한다.

Comments