티스토리 뷰

알고리즘/백준

백준 1219 오민식의 고민 c++

4567은 소수 2025. 2. 2. 16:37

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

(SPFA 연습을 위해 벨만 포드로 풀 수 있는 문제 중 선택을 해보았다.)

 

민식이는 특정 시작점에서 도착점까지 가는 경로마다 비용이 있고, 노드에 도착하면 돈을 법니다. 

 

문제를 다음과 같이 치환할 수 있습니다. 

a node -> b node 비용 cost, b node 도착 시 버는 돈 earn : a -> b 갔을 때 버는 금액 = earn - cost

도착 지점에 도달했을 때 버는 최종 금액을 최대로 하도록 하여라. 

 

시작점에서도 버는 금액이 포함되는 건지가 햇갈렸는데 test case 5번을 보면 포함됨을 알 수 있습니다. 

(7만큼 0번에서 번다, 0번에서 0번으로 갈 때는 10의 cost가 든다. => 답 7 => 처음 0번 시작할 때부터 7만큼 벌고 시작함)

 

cost가 더 큰 경우, 버는 금액이 음수이므로, 벨만 포드 또는 SPFA로 풀 수 있고, SPFA로 풀었습니다. 

 

로직은 다음과 같습니다. 

1. 도착지점까지 갈 수 있는 지 체크한다. (노드 50개, 엣지 50개 밖에 없으므로 별 무리 없음) (gg 체크)

2. SPFA로 각 노드까지 최대 버는 금액을 계산한다. (코드 상 dist) (일반적으로는 최소 거리이지만, 업데이트 기준만 max로 업데이트 하면 됨) 

3. 업데이트된 노드가 큐에 존재하는 지 체크한다. 큐에 없는 경우만 고려한다. (벨만 포드와의 차이점) (큐에 있는 거는 어짜피 다음 차례에 해당 노드 기준으로 업데이트 진행하므로 중복해서 계산할 필요 없음. 원하는 값 업데이트만 하면 됨) 

4. 사이클 발생하는 지 체크한다. 단, 단방향이므로 사이클이 발생한 노드에서 도착 노드까지 경로가 없을 수도 있다. 

사이클이 있으면서 도착지점까지 경로 있으면 Gee를 출력한다. 

5. 사이클 없으면 다음 기준 노드를 큐에 추가한다. 

 

코드는 아래와 같습니다. 

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

using namespace std;

// SPFA로 계산하면서 버는 것까지 고려해서 업데이트 여부 결정해야 함 
// 음수 사이클 대신 무한 사이클 여부 고려해야 함 

int n, m;
int start, finish;
long long inf = -987654321;

vector<pair<int, long long>> graph[50];
queue<int> q;
bool inQueue[50];
long long earn[50];
long long dist[50];
int cycleCount[50];

void init() {
    cin >> n >> start >> finish >> m;

    int from, to;
    long long cost;
    for(int i = 0; i < m; i++) {
        cin >> from >> to >> cost;
        graph[from].push_back({to, -1 * cost});
    }

    for(int i = 0; i < n; i++) {
        cin >> earn[i];
        inQueue[i] = false;
        dist[i] = inf;
        cycleCount[i] = 0;
    }

    // 버는 금액과 가는 비용 미리 상쇄해서 계산 
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < graph[i].size(); j++) {
            graph[i][j].second += earn[graph[i][j].first];
        }
    }
}

bool possible(int startNode, int finishNode) {
    // gg case 체크 
    queue<int> que; 
    bool visited[50];
    for(int i = 0; i < n; i++) visited[i] = false;

    que.push(startNode);
    visited[startNode] = true;

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

        for(int i = 0; i < graph[cur].size(); i++) {
            int next = graph[cur][i].first;
            if(visited[next] == false) {
                que.push(next);
                visited[next] = true;
            }
        }
    }

    // 방문할 수 있는 지 여부 체크 
    if(visited[finishNode]) return true;
    return false;
}

int SPFA() {

    q.push(start);
    dist[start] = earn[start];
    inQueue[start] = true;
    cycleCount[start] = 1;

    while(!q.empty()) {
        int cur = q.front();
        q.pop();
        inQueue[cur] = false;

        long long d = dist[cur];

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

            // 업데이트 되는 경우
            if(dist[next] < (d + cost)) {
                dist[next] = (d + cost);

                // 다음 노드 큐에 없음
                if(inQueue[next] == false) {

                    // 사이클 있는 경우 (무한대 증가 가능)
                    // 사이클 있는데에서 finish까지 가는 경로 있으면 -1 리턴 
                    // 사이클 없으면 큐에 넣기 
                    if((cycleCount[next] + 1) >= n) {
                        // Gee case
                        bool chk = possible(next, finish);
                        if(chk) return -1;
                    } else {
                        q.push(next);
                        inQueue[next] = true;
                        cycleCount[next]++;
                    }
                }
            }
        }
    }

    // 도착 못 함 
    // gg case
    if(dist[finish] == inf) {
        return 0;
    }

    return 1;
}

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

    init();

    bool chk = possible(start, finish);
    if(chk == false) {
        cout << "gg";
        return 0;
    }

    int res = SPFA();
    if(res == -1) cout << "Gee";
    else if(res == 0) cout << "gg";
    else cout << dist[finish];

    return 0;
}

 

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

백준 11408 열혈강호 5  (0) 2025.02.06
6086 최대 유량 c++  (0) 2025.02.02
백준 11657 c++ (SPFA)  (0) 2025.02.02
백준 5670 휴대폰 자판  (1) 2024.03.26
백준 11438 LCA 2 C++  (3) 2024.03.07
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
TAG
more
«   2025/02   »
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
글 보관함