본문 바로가기

알고리즘/백준 문제 풀이

[C++] 6786번 Blood Distribution

728x90

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


 

25/12/14

 

 

최대 유량 기초 문제이다. 모델링을 잘 하는 것이 관건이다.


 

문제 접근 방식:

 

 

문제를 정리하면 다음과 같다.

 

1. 모든 RH- "피"는 RH+, RH- "사람" 상관 없이 줄 수 있음.

2. O형 "피"는 A, B, AB, O "사람" 상관 없이 줄 수 있음.

3. A형 "피"는 A, AB "사람" 상관 없이 줄 수 있음.

4. B형 "피"는 B, AB "사람" 상관 없이 줄 수 있음.

5. AB형 "피"는 AB형 "사람"에게 줄 수 있음.

 

따라서, 일종의 이분 그래프처럼 "피"에서 "사람"으로 향하는 그래프를 모델링 할 수 있다.

그림으로 그리면 다음과 같다.

 

이때 간선의 용량을 정해야 한다.

 

소스에서 각 피로 향하는 간선의 용량은 문제에서 주어지는 피의 개수만큼 정해주고, 각 사람에서 싱크로 향하는 간선의 용량은 사람의 명수만큼 정해주면 된다.

 

그리고 그 외의 간선들의 용량은 INF로 처리하자.


아래는 내가 위의 접근 방식과 같이 작성한 C++ 코드이다. 더보기를 누르면 확인할 수 있다.

더보기
// 6786번 Blood Distribution
// 최대 유량 알고리즘 - 에드몬드 카프
#include <iostream>
#include <vector>
#include <queue>

using namespace std;
#define INF 2'147'483'647
#define fastio ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define endl '\n'

struct Edge{
    int target, capacity, flow; // 타겟, 용량, 유량
    Edge* reverse; // 역방향 간선 참조하는 포인터
    int residualCapacity() const { // 잔여 유량 계산
        return capacity - flow;
    }
    void push(int amount){ // 유량 흘리기(갱신)
        flow += amount;
        reverse->flow -= amount;
    }
};

void addEdge(int u, int v, int capacity, vector<vector<Edge*>>& graph){
    Edge* uv = new Edge(); // u에서 v로 가는 간선 포인터
    Edge* vu = new Edge(); // v에서 u로 가는 역방향 간선 포인터
    // 정방향 간선 uv 초기화
    uv->target = v;
    uv->capacity = capacity;
    uv->flow = 0;
    uv->reverse = vu;
    // 역방향 간선 vu 초기화
    vu->target = u;
    vu->capacity = 0;
    vu->flow = 0;
    vu->reverse = uv;
    // 정방향과 역방향 둘다 넣어준다.
    graph[u].push_back(uv);
    graph[v].push_back(vu);
}

int BFS(int source, int sink, vector<Edge*>& reversedPath, vector<vector<Edge*>>& graph){
    int N = graph.size();
    vector<int> visited(N, 0);
    vector<Edge*> parent(N); // 경로 역추적하기 위한 parent배열 선언.
    queue<int> Q;
    visited[source] = 1;
    Q.push(source);
    
    bool foundAugmentingPath = false;
    while (!Q.empty() && !foundAugmentingPath){
        int now = Q.front(); Q.pop();
        for (Edge* edge: graph[now]){
            int next = edge->target;
            if (!visited[next] && edge->residualCapacity() > 0){ // 방문하지 않았고, 잔여 용량이 남아있다면
                visited[next] = 1;
                parent[next] = edge;
                Q.push(next);
                if (next == sink){ // sink로 도달가능하다면
                    foundAugmentingPath = true;
                    break;
                }
            }
        }
    }
    
    if (!foundAugmentingPath){
        return 0; // 더 이상 흘릴 유량이 없다.
    }

    reversedPath.clear();
    int Flow = INF;
    // 역방향 간선타고 가면서 흘릴 Flow 갱신
    for (Edge* edge = parent[sink]; edge != nullptr; edge = parent[edge->reverse->target]){
        Flow = min(Flow, edge->residualCapacity());
        reversedPath.push_back(edge);
    }
    return Flow;
}

int EdmondKarp(int source, int sink, vector<vector<Edge*>>& graph){
    int totalFlow = 0;
    vector<Edge*> reversedPath;
    while (int flow = BFS(source, sink, reversedPath, graph)){
        for (Edge* edge : reversedPath){
            edge->push(flow);
        }
        totalFlow += flow;
    }
    return totalFlow;
}

int main(void){
    fastio

    vector<vector<Edge*>> G(18);
    vector<int> blood(8);
    for (int i = 0; i < 8; ++i) cin >> blood[i];
    vector<int> patient(8);
    for (int i = 0; i < 8; ++i) cin >> patient[i];
    for (int i = 0; i < 8; ++i){
        addEdge(0, i+1, blood[i], G);
    }
    for (int i = 0; i < 8; ++i){
        addEdge(9+i, 17, patient[i], G);
    }
    vector<pair<int, int>> edges = {{1, 9}, {1, 10}, {1, 11}, {1, 12}, {1, 13}, {1, 14}, {1, 15}, {1, 16},
                                    {2, 10}, {2, 12}, {2, 14}, {2, 16},
                                    {3, 11}, {3, 12}, {3, 15}, {3, 16},
                                    {4, 12}, {4, 16},
                                    {5, 13}, {5, 14}, {5, 15}, {5, 16},
                                    {6, 14}, {6, 16},
                                    {7, 15}, {7, 16}, {8, 16}};
    for (auto p : edges){
        addEdge(p.first, p.second, INF, G);
    }
    cout << EdmondKarp(0, 17, G);
    return 0;
}
728x90

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

[C++] 14398번 피타고라스 수  (0) 2025.12.26
[C++] 16590번 KMP  (0) 2025.12.24
[C++] 22695번 Spirograph  (0) 2025.11.08
[C++] 24558번 Downsizing  (0) 2025.10.21
[C++] 34041번 회전체와 쿼리  (0) 2025.10.20