백준 6086번 최대 유량
문제 링크 : https://www.acmicpc.net/problem/6086
문제풀이
사용한 알고리즘 : Network Flow
강산님의 블로그 글을 참고하여 작성하였습니다.
(0) 생각
네트워크 유량을 쓸 줄 아는가 묻는 문제였습니다.
에드몬드 카프 알고리즘을 사용하여 풀이하였습니다.
(1) 코드 16~18
각 노드간 간선을 저장할 vector 배열을 만들어 주었습니다.
'c[i][j] : i->j 간선의 용량' ,
'f[i][j] : i->j 간선의 유량' 이라고 설정해 주었습니다.
(2) 코드 43~78
에드몬드 카프 알고리즘을 구현해줍니다.
시작점(S)에서 도착점(T)까지 경로가 있는지 BFS로 탐색 해줍니다.
경로가 존재 한다면, 해당 경로 상의 가장 작은 '용량-유량' 값을 구해주어, 이 값을 경로 상의 모든 간선에 더해줍니다.
이때 구하고자 하는 답에도 해당 값을 더해줍니다.
S ~ T 사이의 경로가 없을 때까지 반복해줍니다.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <queue> | |
#include <vector> | |
using namespace std; | |
const int MAX_V = 52; // 알파벳 대,소문자 총 52 개 | |
const int INF = 1000000000; | |
// 알파벳 -> 숫자 변환 | |
int CtoI(char c) { | |
// index: 대문자 = 0~25 , 소문자 = 26~51 | |
if (c <= 'Z') return c - 'A'; // 대문자가 소문자보다 아스키 코드 작다. | |
return c - 'a' + 26; | |
} | |
int N; // 간선개수 | |
vector<int> adj[MAX_V]; // 간선 | |
int c[MAX_V][MAX_V]; // c[i][j] : i -> j 가는 용량 | |
int f[MAX_V][MAX_V]; // f[i][j] : i -> j 가는 유량 | |
int Prev[MAX_V]; // 지나온 경로 기억하기 위해 | |
int main() {ios_base::sync_with_stdio(false); cout.tie(NULL); cin.tie(NULL); | |
cin >> N; | |
for (int i = 0; i < N; ++i) { | |
char a, b; | |
int u, v, w; | |
cin >> a >> b >> w; | |
u = CtoI(a); | |
v = CtoI(b); | |
c[u][v] += w; | |
c[v][u] += w; // 역방향 간선에도 용량 추가 | |
adj[u].push_back(v); | |
adj[v].push_back(u); | |
} | |
int total = 0; // 총 유량 (구하고자 하는 답 ) | |
int S = CtoI('A'); // 소스 ( 물 나오는 데 ) | |
int T = CtoI('Z'); // 싱크 ( 물 들어가는 데) | |
while (1) { // 증가 경로 못찾을 때까지 반복 | |
// BFS 로 증가경로 찾는다. | |
fill(Prev, Prev + MAX_V, -1); | |
queue<int> Q; | |
Q.push(S); // 시작점 | |
while (!Q.empty() && Prev[T] == -1) { | |
int curr = Q.front(); | |
Q.pop(); | |
for (int next : adj[curr]) { | |
// c[i][j] - f[i][j] > 0 : i->j 가능한 용량 여유가 남았는가? | |
// prev[next] = -1 : 아직 방문되지 않은 점인가? | |
if (c[curr][next] - f[curr][next] > 0 && Prev[next] == 0) { | |
Q.push(next); | |
Prev[next] = curr; // 경로 기억 | |
if (next == T) break; // T 도착 | |
} | |
} | |
} | |
// T 로 가는 경로가 더 없으면 루프 탈출 | |
if (Prev[T] == -1) break; | |
// 차단간선 ( 경로상 가장 허용치가 낮은 간선 ) 찾기 | |
int Flow = INF; | |
for (int i = T; i != S; i = Prev[i]) // Prev[i] -> i 였음을 기억 | |
Flow = min(Flow, c[Prev[i]][i] - f[Prev[i]][i]); | |
// 찾은 flow 만큼 해당 경로에 유량을 흘려줌 | |
for (int i = T; i != S; i = Prev[i]) { | |
f[Prev[i]][i] += Flow; | |
f[i][Prev[i]] -= Flow; // 역방향 간선은 가능한 용량 증가!! (즉 유량 감소) | |
} | |
// 총 유량 값 증가 | |
total += Flow; | |
} | |
cout << total << '\n'; | |
return 0; | |
} |
댓글
댓글 쓰기
긴 글 읽어주셔서 감사합니다.
궁금한게 있으시다면 댓글 달아주세요!