알고리즘/문제 풀이

백준 11657번: 타임머신

Themion 2022. 1. 25. 14:40

https://www.acmicpc.net/problem/11657

 

11657번: 타임머신

첫째 줄에 도시의 개수 N (1 ≤ N ≤ 500), 버스 노선의 개수 M (1 ≤ M ≤ 6,000)이 주어진다. 둘째 줄부터 M개의 줄에는 버스 노선의 정보 A, B, C (1 ≤ A, B ≤ N, -10,000 ≤ C ≤ 10,000)가 주어진다. 

www.acmicpc.net

각 도시를 노드로, 버스를 에지 혹은 순간이동 혹은 타임머신으로 본다면, 가중치가 음인 에지가 존재하고 가중치가 음인 사이클이 존재하는지 찾는 문제이므로 벨만-포드 알고리즘을 이용해 해결할 수 있다.

#include <cstdio>
#include <queue>
#include <vector>

using namespace std;

typedef pair<int, int> edge;
typedef long long ll;

#define MAX_N 500
#define INF 0x3f3f3f3f3f3f3f3f

#define _node first
#define _cost second

// N: 노드의 수, M: 에지의 수
int N, M;
// cost[i]: 노드 i로 이동하는 데에 걸리는 최소 시간
ll cost[MAX_N + 1] = { 0, };
// 벨만 포드에서 각 step때 탐색을 시작할 노드의 집합
queue<int> q({1});
// v[i]: 노드 i에서 시작하는 에지의 집합
vector<edge> v[MAX_N + 1];

// node에서 에지를 하나 사용해 그래프를 탐색
bool push(short node) {
    // 비용을 하나라도 단축시켰다면 true
    bool ret = false;
    // 시작 노드에서 node까지 이동한 뒤 에지를 하나 사용한 비용
    ll cost_;

    // node가 q.front이므로 q에서 node를 pop한다
    q.pop();

    // node에서 시작하는 모든 에지에 대해
    for (edge e : v[node]) {
        // 현재 에지를 사용해 얻을 수 있는 비용을 계산
        cost_ = cost[node] + e._cost;
        // 비용 감소가 불가능하다면 현재 에지를 건너뛴다
        if (cost[e._node] <= cost_) continue;

        // 비용을 감소시킨 뒤 현재 에지를 사용해 도착한 노드를 q에 push
        cost[e._node] = cost_;
        q.push(e._node);
        // 비용의 변화가 발생했으므로 ret에 true를 저장
        ret = true;
    }

    // 값이 바뀌었는지 여부를 반환
    return ret;
}

// 그래프가 음의 가중치를 갖는다면 true
bool bellman_ford() {
    // 에지를 1개부터 N - 1개까지 사용할 때의 최소 비용을 계산
    for (int i = 1; i < N; i++)
        // 에지를 i - 1개 사용했을 때 도달한 노드에서 에지를 하나 사용해 이동
        for (int len = q.size(); len--; ) push(q.front());

    // 에지를 총 N - 1개 사용한 이후에 갱신 가능한 거리가 있다면
    // 음의 가중치를 갖는 사이클이 존재하므로 true를 반환
    for (int len = q.size(); len--; ) if (push(q.front())) return true;
    // 거리를 갱신하지 못했다면 음의 가중치를 갖는 사이클이 존재하지 않는다
    return false;
}

int main() {
    // A, B, C: 노드 A에서 B로 이동하며 비용이 C인 에지
    int A, B, C;

    // 문제의 조건을 입력받으며 v에 에지를 각각 push
    scanf("%d %d", &N, &M);
    for (int i = 0; i < M; i++) {
        scanf("%d %d %d", &A, &B, &C);
        v[A].push_back({B, C});
    }
    // 시작 노드를 제외한 모든 노드로 이동하는 비용을 INF로 초기화
    for (int i = 2; i <= N; i++) cost[i] = INF;

    // 벨만-포드를 이용해 가중치가 음인 사이클을 발견했다면 -1을 출력
    if (bellman_ford()) printf("-1\n");
    // 그렇지 않다면 각 노드로 이동할 수 있을 때 그 최소 비용을,
    // 이동할 수 없다면 -1을 출력
    else for (int i = 2; i <= N; i++)
        printf("%lld\n", cost[i] != INF ? cost[i] : -1);

    return 0;
}

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

백준 23349번: 졸업 사진  (0) 2022.02.03
백준 10478번: 단위  (0) 2022.01.26
백준 1520번: 내리막 길  (0) 2022.01.25
백준 19598번: 최소 회의실 개수  (0) 2022.01.23
백준 19598번: 최소 회의실 개수  (0) 2022.01.23