티스토리 뷰

알고리즘/백준

6086 최대 유량 c++

4567은 소수 2025. 2. 2. 21:47

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

 

주어진 파이프를 이용해 A -> Z 로의 최대 유량을 계산하는 문제입니다. 

주의할 점 : 소문자도 된다. 병렬로 연결도 가능하다. 양방향 연결이다. 

 

Edmonds Karp 알고리즘을 이용해 최대 유량을 계산하면 되지만, 양방향성과 병렬 가능에 주의를 해야 합니다. 

기본적으로는 가상의 역방향 엣지를 추가하고, cap은 0으로 유지하여 Edmonds-Karp 알고리즘을 수행하지만, 

양방향이므로, 역방향 엣지와 cap을 동일하게 잡아줍니다. 

그리고 병렬처리가 가능하므로 cap을 누적하여 추가해줍니다. 

 

불필요한 bfs 탐색을 방지하기 위해 vector에 누적해서 동일한 node를 추가하는 것 대신, set으로 연결된 노드를 관리하였습니다. 

 

최대 유량 계산 시 사용되는 역방향 유량은 양방향 그래프와 상관없이, 현재 체크된 증가 경로 상의 유량을 계산할 때 사용하는 것이므로, 동일하게 음수 처리를 해주어야 합니다. 

 

코드는 아래와 같습니다. 

#include <iostream>
#include <vector>
#include <set>
#include <queue>
#include <algorithm>

using namespace std;

int n;
int inf = 987654321; // 모든 엣지 다 통과해도 70만

set<int> graph[52]; // A : 0 ~ Z : 25, a : 26 ~ z : 51
// graph[i] : i와 연결된 노드 집합
int cap[52][52]; // [i][j] -> i to j capacity (해당 엣지 용량)
int flow[52][52]; // [i][j] -> i to j flow (현재까지 계산된 유량)

int A = 0; // source
int Z = 25; // sink

int charToInt(char c) {
    if(c >= 'A' && c <= 'Z') return c - 'A';
    else return c - 'a' + 26;
}

void init() {

    for(int i = 0; i < 52; i++) {
        for(int j = 0; j < 52; j++) {
            cap[i][j] = 0;
            flow[i][j] = 0;
        }
    }

    cin >> n;

    char c1, c2;
    int v, node1, node2;
    for(int i = 0; i < n; i++) {
        cin >> c1 >> c2 >> v;
        node1 = charToInt(c1);
        node2 = charToInt(c2);

        graph[node1].insert(node2);
        graph[node2].insert(node1); // 문제 상 양방향 그래프 

        cap[node1][node2] += v; // 병렬로 연결 가능하므로 누적해서 cap 추가 
        cap[node2][node1] += v;
    }
}

int calc(int source, int sink) {
    // edmonds-karp

    int res = 0;

    while(true) {
        // 증가 경로 없을 때까지 반복

        vector<int> parents(52, -1); // 증가 경로 상 부모 없으면 -1 
        queue<int> q; // 증가 경로 계산 시 사용될 큐 (bfs)

        parents[source] = source;
        q.push(source);

        while(!q.empty() && (parents[sink] == -1)) {
            // 다 돌거나 sink 증가 경로에 포함되면 종료 

            int cur = q.front();
            q.pop();

            // 잔여 용량 있고 증가 경로에 포함 안 된 경우, 증가 경로에 추가 
            for(auto next : graph[cur]) {
                int remain = cap[cur][next] - flow[cur][next];
                if((remain > 0) && (parents[next] == -1)) {
                    q.push(next);
                    parents[next] = cur;
                }
            }
        }

        // sink 증가 경로에 없음. 즉, 더 이상 증가 경로 없음 
        if(parents[sink] == -1) break;

        // 증가 경로 중 잔여 용량 최소값만큼 유량 추가 
        int node = sink;
        int p; // 부모 노드 계산용 
        int amount = inf;
        while(node != source) {
            p = parents[node];
            amount = min(amount, cap[p][node] - flow[p][node]);
            node = p;
        }

        // 계산된 유량 추가
        node = sink;
        while(node != source) {
            p = parents[node];
            flow[p][node] += amount;
            flow[node][p] -= amount; 
            node = p;
        }

        // 전체 유량에 추가 
        res += amount;
    }

    return res;
}

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

    init();
    cout << calc(A, Z);
}

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

백준 11409 열혈강호 6  (0) 2025.02.06
백준 11408 열혈강호 5  (0) 2025.02.06
백준 1219 오민식의 고민 c++  (0) 2025.02.02
백준 11657 c++ (SPFA)  (0) 2025.02.02
백준 5670 휴대폰 자판  (1) 2024.03.26
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
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
글 보관함