[ 백준 ] 1865 / 웜홀

金弘均·2021년 9월 14일
0

Baekjoon Online Judge

목록 보기
7/228
post-thumbnail
post-custom-banner

# Appreciation

/*
 * Problem :: 1865 / 웜홀
 *
 * Kind :: Bellman-Ford
 *
 * Insight
 * - 한 지점에서 출발을 하여서 시간여행을 하기 시작하여
 *   다시 출발을 하였던 위치로 돌아왔을 때,
 *   출발을 하였을 때보다 시간이 되돌아가 있는 경우가 있는지 없는지
 *   + 음수의 사이클이 있는지 없는지
 *     # 벨만포드 알고리즘을 적용하자
 *
 * - 다익스트(Dijkstra)는 간선의 가중치가 모두 0 또는 양수이므로
 *   다익스트라 알고리즘을 적용해서 최단거리르 구하는 도중,
 *   아직 방문하지 않은 상태인 모든 노드들 중 출발점으로부터의 거리가 제일 짧은 노드가
 *   출발점으로부터 그 노드까지의 최단거리가 된다
 *   + 하지만 간선들 중 하나라도 가중치가 음수인 간선이 있다면,
 *     아직 방문하지 않은 상태인 모든 노드들 중 출발점으로부터의 거리가 제일 짧은 노드가
 *     출발점으로부터 그 노드까지의 최단거리가 아닐 수도 있다!
 *     # (u,v:t) = u번 노드 -> v번 노드, 거리 t 라면
 *       (1,2:1), (2,3:2) 로 1->2->3 인 (1,3:3) 이 최단거리인줄 알았으나
 *       (2,4:3), (4,3:-2) 로 1->2->4->3 인 (1,3:2) 이 실제 최단거리일 수 있다
 *       => 때문에, 최단거리를 구하는 도중 어떤 노드가 현재 최단거리인지 알 수 없고
 *          결국 모든 경우의 수를 따져봐야 한다
 *
 * - N 개의 노드가 있다면 가능한 최단거리는 (N-1)개의 간선으로 이루어진다
 *   모든 경우의 수를 따지려면, 모든 간선을 (N-1)번 Relaxation 해야한다
 *   즉, 모든 간선을 한번 순회하며 Relaxation 하고
 *   한 번이라도 Relaxation 이 일어났다면, 모든 간선을 다시 순회하며 Relaxation 하고
 *   또 한 번이라도 Relaxation 이 일어났다면, 모든 간서을 다시 순회하며 Relaxation 하고...
 *   이렇게 (N-1)번 반복해야 한다
 *   + 게산이 끝나면 "음의 사이클이 없는 한" 현재 계산된 각 노드별 최단거리가 실제 최단거리이다
 *     하지만 여기서 모든 간선을 다시 한번 순회하며 Relaxation 해준다
 *     음의 사이클이 있는지 확인하기 위해서이다
 *     # 만약, 여기서 Relaxation 이 한 번이라도 일어난다면,
 *       음의 사이클이 존재한다는 뜻이다
 *       N 개의 간선으로 이루어진 최단거리가 존재한다는 뜻이기 때문이다!
 *       즉, 중복된 간선이 있다는 뜻이고 간선이 중복되기 위해서는 사이클이 있어야 하기 때문에
 *       이 사이클은 음의 사이클일 수밖에 없다
 *       => 종합하자면, 모든 간선을 (N-1)번 순회하며 Relaxation 해주고
 *          한번 더 모든 간선을 순회하며 Relaxation 해줌으로써
 *          Relaxation 이 일어나는지 여부로 음의 사이클의 존재 여부를 알 수 있다
 *
 * Point
 * - 본 코드에서는 s=시작점, e=도착점, t=걸리는시간 을 뜻하는데,
 *   Relaxation 조건을 if (D[s] + t < D[e]) 가 아닌
 *   if (D[s] != INF && D[s] + t < D[e]) 로 주면 <= 시작점이 INF 인지 확인하는 조건 추가
 *   s == e, t < 0 일 때의 음의 사이클을 확인하지 못하게 되어 틀리게 된다
 *
 * - 오랜만이라 그런지 다익스트라, 벨만포드 둘 다 기억이 나질 않네... ㅎㅎ
 *   + 꾸준히 복습하고 기록하자~
 */

# Code

//
//  BOJ
//  ver.C++
//
//  Created by GGlifer
//
//  Open Source

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

#define endl '\n'

// Set up : Global Variables
struct Edge { int s, e, t; };
#define INF (int)(1e9+7)

// Set up : Functions Declaration
/* None */


int main()
{
    // Set up : I/O
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    // Set up : Input
    int TC; cin >> TC;

    while (TC--) {
        int N, M, W;
        cin >> N >> M >> W;
        vector<Edge> E;
        for (int i=0; i<M; i++) {
            int s, e, t;
            cin >> s >> e >> t;
            E.push_back({s, e, t});
            E.push_back({e, s, t});
        }
        for (int i=0; i<W; i++) {
            int s, e, t;
            cin >> s >> e >> t;
            E.push_back({s, e, -t});
        }

        // Process
        int D[N+1];
        fill(&D[1], &D[N+1], INF);
        D[1] = 0;

        for (int i=0; i<N-1; i++) {
            for (Edge edge : E) {
                auto [s, e, t] = edge;
                if (D[s] + t < D[e]) {
                    D[e] = D[s] + t;
                }
            }
        }

        bool isPossible = false;
        for (Edge edge : E) {
            auto [s, e, t] = edge;
            if (D[s] + t < D[e]) {
                isPossible = true;
                break;
            }
        }

        // Control : Output
        cout << ((isPossible) ? "YES" : "NO") << endl;
    }
}

// Helper Functions
/* None */
profile
이런 미친 게임을 봤나! - 옥냥이
post-custom-banner

0개의 댓글