티스토리 뷰

알고리즘/백준

백준 11657 c++ (SPFA)

4567은 소수 2025. 2. 2. 03:05

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

(SPFA 공부하고 적용해봤는데 타입 잘 못 잡기 + INF 케이스 오버플로우 방지 안함에 의해 수없이 틀려버렸다....)

 

SPFA 알고리즘은 벨만-포드 알고리즘의 응용으로 벨만-포드 알고리즘이 모든 정점에 대해서 거리를 업데이트하는 것 대신, 

거리 업데이트되는 노드를 대상으로만 알고리즘을 진행하는 방식이다. 

알고리즘을 진행할 노드를 큐로 관리하며, 큐에 해당 노드가 존재하는 지 체크하는 배열을 별도로 두어 중복 계산을 방지한다. 

 

벨만-포드 알고리즘이 복잡도가 O(VE) 이지만, SPFA는 dense 그래프가 아니면 O(V+E) 수준이므로, 벨만 포드보다 빠르게 음수가 있는 가중치에서도 최단 거리 계산에 접목할 수 있다.

그리고 큐에 전체 노드 개수만큼 추가된 경우 (즉, 거리 업데이트 및 알고리즘 수행을 위해 n번 해당 노드를 방문) cycle이 발생한 경우이므로 사이클 체크 또한 가능하다. 

 

위 문제에 대한 코드는 아래와 같다. 

#include <iostream>
#include <queue>
#include <vector>

using namespace std;

int n, m; // n <= 500
int from, to; 
long long cost; // 문제 상 a,b,c 
long long INF = 9223372036854775807; // 무한대

vector<pair<int, long long>> graph[501]; // [i = from] : {to, val}
vector<bool> inQueue; // 해당 노드 큐에 있는 지 유무 체크
vector<long long> dist; // 시작 노드에서 해당 노드까지 최단 거리
vector<int> cycleCount; // 무한 루프 체크용

void init() {
    cin >> n >> m;
    for(int i = 0; i < m; i++) {
        cin >> from >> to >> cost;
        graph[from].push_back({to, cost});
    }

    inQueue.resize(n + 1, false);
    dist.resize(n + 1, INF);
    cycleCount.resize(n + 1, 0);
}

// SPFA
// 벨만 포드에서 모든 정점 기준으로 거리 업데이트하는 것 대신 
// 갱신된 노드 대상으로만 거리 업데이트 진행
// 특정 노드가 전체 노드 개수만큼 큐에 추가되면 한 바퀴 다 돌고도 갱신되는 것이므로 무한 루프 지점임
// 복잡도 : worst O(V*E), 대부분 O(V+E)

int SPFA() {
    queue<int> q;

    q.push(1); // 문제 상 시작 1번 노드 
    inQueue[1] = true;
    dist[1] = 0;
    cycleCount[1]++;

    while(!q.empty()) {
        int cur = q.front();
        q.pop();

        inQueue[cur] = false;
        long long d = dist[cur];

        // 갱신 안 된 노드 
        if(d == INF) continue;

        for(int i = 0; i < graph[cur].size(); i++) {
            int next = graph[cur][i].first;
            long long cost = graph[cur][i].second;

            long long toNext = dist[next]; // 시작점에서 next까지 현재 계산된 거리
            long long nd = d + cost; // cur 거쳐서 갈 때 거리

            // 거리 업데이트 하는 경우 
            if(toNext > nd) {
                // 거리 업데이트 
                dist[next] = nd;

                // 큐에 없는 경우 
                if(inQueue[next] == false) {
                    // 큐에 추가
                    q.push(next);
                    inQueue[next] = true; 
                    // 카운트 추가
                    cycleCount[next]++;

                    // cycle 발생하는 경우
                    if(cycleCount[next] >= n) {
                        return -1;
                    }
                }
            }
        }
    }

    return 0;
}

int main() {
    ios_base :: sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    init();
    
    int res = SPFA();
    if(res == -1) {
        cout << -1 << '\n'; 
        return 0;
    }

    for(int i = 2; i <= n; i++) {
        // 해당 지점까지 못 가는 경우 
        if(dist[i] == INF) cout << -1 << '\n';
        else cout << dist[i] << '\n';
    }

    return 0;
}

'알고리즘 > 백준' 카테고리의 다른 글

6086 최대 유량 c++  (0) 2025.02.02
백준 1219 오민식의 고민 c++  (0) 2025.02.02
백준 5670 휴대폰 자판  (1) 2024.03.26
백준 11438 LCA 2 C++  (3) 2024.03.07
백준 3665 최종 순위 Rust  (0) 2024.02.12
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
TAG
more
«   2025/03   »
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
글 보관함