λ€νΈμν¬ μ λ(Network Flow)
κ·Έλν κ°λ μ€ νλλ‘ λ€νΈμν¬ μ λμ μ΄ν΄νκΈ° μν΄μ λ¨Όμ μλ‘μ΄ μ©μ΄μ μ λ κ·Έλνμ νΉμ§λΆν° μμμΌ νλ€.
- μ λ κ·Έλνλ λ³΄ν΅ λ°©ν₯ κ·ΈλνμΈ κ²½μ°κ° λ§λ€.
- κ·Έλνμ κ°μ 거리, μκ°μ΄λ κ°μ€μΉ λμ μ©λ(Capacity)μ΄λΌ νλ€.
- λ μ μ u, vλ₯Ό μλ κ°μ (u, v)κ° μμ λ, uμμ vλ‘ κ°μ μ μ©λ μ΄νλ§νΌμ μ λ(flow)μ νλ €λ³΄λΌ μ μλ€.
- κ·Έλν λ΄ μλ‘ λ€λ₯Έ μμ€(S, Source) μ μ κ³Ό μ±ν¬(T, Sink) μ μ μ΄ μλλ°, μμ€ μ μ μ μ λμ λ°μμν¨λ€.
- μμ€ μ μ μΈ μ μ λ€μ μμ μ΄ λ°μ μ λλ§νΌλ§ λ€μ νλ €λ³΄λΈλ€.
- κ° κ°μ μ νλ₯΄λ μ λμ κ·Έ κ°μ μ μ©λμ λμ μ μλ€.
- κ°μ (u, v)λ‘ μ λμ΄ νλ₯΄κ³ μλ€λ©΄ μλ°©ν₯μΈ κ°μ (v, u)μ μμ μ λμ΄ κ·Έλ§νΌ νλ₯΄κ³ μλ€.
λ€νΈμν¬ μ λμ κ·Έλν λ΄ μμ€ μ μ μμ μ λμ λ°μμμΌ κ°μ μ ν΅ν΄ μ±ν¬ μ μ μ λλ¬μν€λ κ²μ΄ λͺ©νμ΄λ€.
μ΅λ μ λ μκ³ λ¦¬μ¦
Sμμ Tλ‘ μ λ 1μ 보λ΄λ©΄, μ λ 1μ λ³΄λΌ μ μμ μ λλ‘ μ©λμ΄ μΆ©λΆν κ°μ μ μ°Ύμ TκΉμ§ 보λΈλ€. μ΄λ, μ λμ νλ €λ³΄λ΄λ κ²μ λμ€μ λ©μΆ°μλ μ λλ€. λ°λμ TκΉμ§ λλ¬ν΄μΌ μ ν¨νλ€.
μ λ 1(S-A)μ λ³΄λΈ ν λ€μ νλ² μ λ 1(S-B)κ³Ό μ λ 4(S-C)λ₯Ό λ³΄λΈ ν κ²°κ³Όμ΄λ€.
μ§κΈκΉμ§ Sμμ Tλ‘ λ³΄λΈ μ λμ ν©μ 6μΈλ°, μ¬κΈ°μ λ λ§μ μ λμ Tλ‘ λ³΄λΌ μ μμκΉ? λΆκ°λ₯νλ€. μλνλ©΄ κ²½λ‘μ μν κ°μ (D, T)μ λ μ΄μ μ©λμ΄ λ¨μμμ§ μλ€.
λ λ§μ μ λμ 보λ΄λ λ°©λ²μ μμκΉ?
μ²μ κ²½λ‘κ° μλ μ΅λ μ λ μκ³ λ¦¬μ¦μ λ°λΌ κ²½λ‘λ₯Ό μ°ΎμΌλ©΄ λ λ§μ μ λμ λ³΄λΌ μ μκ² λλ€.
1. ν¬λ νμ»€μ¨ μκ³ λ¦¬μ¦(Ford-Fulkerson algorithm)
DFSλ‘ κ΅¬νν μκ³ λ¦¬μ¦μΌλ‘ κΈ°μ΄κ° λλ μκ³ λ¦¬μ¦μ΄λ€.
1) κΈ°λ³Έ μ리
- Sμμ Tλ‘ κ°λ μ¦κ° κ²½λ‘ μ€ μ무거λ νλλ₯Ό μ°Ύλλ€.
- μ¦κ° κ²½λ‘ μ€ μ°¨λ¨ κ°μ μ μ°Ύλλ€. μ΄ κ°μ μ κ²½λ‘μμμ F(c(u, v) - f(u, v)) κ°μ΄ μ΅μμΈ κ°μ μ΄λ€.
- κ²½λ‘ λ΄ λͺ¨λ κ°μ μ F μ λμ μΆκ°νλ€. μ¦, Sμμ Tλ‘ μ΄ κ²½λ‘λ₯Ό λ°λΌμ Fλ§νΌμ μ λμ μλ‘ νλ €λ³΄λΈ κ²μ΄λ€.
- μ΄λ, μμ μ λλ λ§μ‘±ν΄μΌ νκΈ° λλ¬Έμ μλ°©ν₯λ μ΄λ κ°λ₯νλ€.
- μ κ³Όμ μ λ μ΄μ μ¦κ° κ²½λ‘κ° μμ λκΉμ§ λ°λ³΅νλ€.
2) λ¬Έμ
https://www.acmicpc.net/problem/6086
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
int getID(char c)
{
if (c <= 'Z')
return c - 'A';
return c - 'a' + 26;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int N; // κ°μ μ
int c[52][52]; // c[i][j]: iμμ jλ‘ κ°λ κ°μ μ μ©λ
int f[52][52]; // f[i][j]: iμμ jλ‘ νμ¬ νλ₯΄λ μ λ
vector<int> map[52]; // μΈμ 리μ€νΈ
cin >> N;
for (int i = 0; i < N; i++) {
char u, v;
int w;
cin >> u >> v >> w;
int U = getID(u), V = getID(v);
map[U].push_back(V);
map[V].push_back(U); // μλ°©ν₯ κ°μ
c[U][V] += w;
c[V][U] += w;
}
int maximumFlow = 0, s = 0, t = 25;
while (true) {
int visit[52]{};
fill(visit, visit + 52, -1);
queue<int> q;
q.push(s);
while (!q.empty()) {
int front = q.front();
q.pop();
for (int i = 0; i < map[front].size(); i++) {
int next = map[front][i];
if (c[front][next] - f[front][next] > 0 && visit[next] == -1) {
q.push(next);
visit[next] = front;
if (next == t)
break;
}
}
}
// μ¦κ° κ²½λ‘κ° μλ€λ©΄ νμΆ
if (visit[t] == -1)
break;
int flow = 1e9;
// μ°¨λ¨ κ°μ μ°ΎκΈ°
for (int i = t; i != s; i = visit[i])
flow = min(flow, c[visit[i]][i] - f[visit[i]][i]);
// μ°¨λ¨ κ°μ μ©λλ§νΌ μ λ ν리기
for (int i = t; i != s; i = visit[i]) {
f[visit[i]][i] += flow;
f[i][visit[i]] -= flow;
}
maximumFlow += flow;
}
cout << maximumFlow;
}
https://www.acmicpc.net/problem/17412
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
int c[401][401];
int f[401][401];
vector<int> map[401];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int N, P;
cin >> N >> P;
while (P--) {
int u, v;
cin >> u >> v;
map[u].push_back(v);
map[v].push_back(u);
c[u][v] = 1;
c[v][u] = 0;
}
int maximumFlow = 0, s = 1, t = 2;
while (true) {
int visit[401]{};
fill(visit, visit + 401, -1);
queue<int> q;
q.push(s);
while (!q.empty()) {
int front = q.front();
q.pop();
for (int i = 0; i < map[front].size(); i++) {
int next = map[front][i];
if (c[front][next] - f[front][next] > 0 && visit[next] == -1) {
q.push(next);
visit[next] = front;
if (next == t)
break;
}
}
}
// μ¦κ° κ²½λ‘κ° μλ€λ©΄ νμΆ
if (visit[t] == -1)
break;
int flow = 1e9;
// μ°¨λ¨ κ°μ μ°ΎκΈ°
for (int i = t; i != s; i = visit[i])
flow = min(flow, c[visit[i]][i] - f[visit[i]][i]);
// μ°¨λ¨ κ°μ μ©λλ§νΌ μ λ ν리기
for (int i = t; i != s; i = visit[i]) {
f[visit[i]][i] += flow;
f[i][visit[i]] -= flow;
}
maximumFlow += flow;
}
cout << maximumFlow;
}
2. μλλͺ¬λ μΉ΄ν μκ³ λ¦¬μ¦(Edmonds-Karp algorithm)
BFSλ‘ κ΅¬νν μκ³ λ¦¬μ¦μΌλ‘ ν¬λ νμ»€μ¨ μκ³ λ¦¬μ¦κ³Ό κΈ°λ³Έ λμμ λμΌνμ§λ§ μκ° λ³΅μ‘λκ° ν¨μ¬ ν¨μ¨μ μ΄λ€.
3. λλ μκ³ λ¦¬μ¦(Dinic's algorithm)
λλΆλΆ μ λ λ¬Έμ λ μμ λ μκ³ λ¦¬μ¦μΌλ‘ ν리μ§λ§ κ°νΉ λ λΉ λ₯Έ μκ° λ³΅μ‘λλ₯Ό μꡬνλ λ¬Έμ κ° μλ€. κ·Έλ¬ν λ¬Έμ λ λλ μκ³ λ¦¬μ¦μ μ¬μ©ν΄μΌ νλ€.
1) κΈ°λ³Έ μ리
- λ 벨 κ·Έλν(level graph)λ₯Ό λ§λ λ€. μ΄λ μ±ν¬μ λλ¬ν μ μλ€λ©΄ μ’
λ£νλ€.
- λͺ¨λ μ μ μ λν΄ μμ€μμλΆν° μ΅λ¨κ±°λ¦¬λ₯Ό λ 벨 κ°μΌλ‘ 맀겨λμ κ·Έλνμ΄λ€.
- μ¬μ μ©λμ΄ μλ κ°μ μ μ¬μ©ν μ μλ€.
- λ 벨 κ·Έλνμμ μ‘΄μ¬νμ§ μλ κ°μ μ μ μ μΌλ‘ νμνλ€.
- λ 벨 κ·Έλνμμ μ°¨λ¨ μ λ(blocking flow)μ μ°Ύμ μ΄μ λμ λνκ³ λ€μ λ 벨 κ·Έλνλ₯Ό λ§λ λ€.
- λ 벨 κ·Έλνμμλ νμ μΈμ νλ©΄μ μμ λ³΄λ€ λ λ²¨μ΄ 1 ν° μ μ μΌλ‘ μ΄λμ΄ κ°λ₯νλ€.
- λ μ΄μ μμ€μμ μ±ν¬λ‘ ν릴 μ μλ μ λμ΄ μκ² λ§λλ μ λ μνλ₯Ό λ§νλ€.
2) λ¬Έμ
https://www.acmicpc.net/problem/13161
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
const int MAX_N = 500;
const int MAX_V = MAX_N + 2;
const int S = MAX_V - 2;
const int E = MAX_V - 1;
const int INF = 1000000000;
vector<int> adj[MAX_V];
int c[MAX_V][MAX_V], f[MAX_V][MAX_V];
int level[MAX_V], work[MAX_V];
// λ 벨 κ·Έλν(level graph) λ§λ€κΈ°
bool LevelGraph()
{
fill(level, level + MAX_V, -1);
level[S] = 0;
queue<int> Q;
Q.push(S);
while (!Q.empty()) {
int curr = Q.front();
Q.pop();
for (int next : adj[curr]) {
// λ 벨κ°μ΄ μ€μ λμ§ μμκ³ κ°μ μ μ¬μ μ©λμ΄ μμ λ μ΄λ
if (level[next] == -1 && c[curr][next] - f[curr][next] > 0) {
level[next] = level[curr] + 1; // nextμ λ 벨μ currμ λ 벨 + 1
Q.push(next);
}
}
}
return level[E] != -1; // μ±ν¬μ λλ¬ κ°λ₯νλ©΄ true
}
// μ°¨λ¨ μ λ(blocking flow) ꡬνκΈ°: currμμ destκΉμ§ μ΅λ flow μ λμ λ³΄λΌ μ μλμ§
int BlockingFlow(int curr, int dest, int flow)
{
if (curr == dest)
return flow;
// μ΄λ―Έ μΈλͺ¨μλ€κ³ νλ¨ν κ°μ μ λ€μ λ³Ό νμκ° μμΌλ―λ‘ work λ°°μ΄λ‘ κ°μ μΈλ±μ€λ₯Ό μ μ₯
for (int& i = work[curr]; i < adj[curr].size(); i++) {
int next = adj[curr][i];
// next λ λ²¨μ΄ curr λ 벨 + 1μ΄κ³ κ°μ μ μ¬μ μ©λμ΄ μμ λ μ΄λ
if (level[next] == level[curr] + 1 && c[curr][next] - f[curr][next] > 0) {
// df: κ²½λ‘μμ κ°μ λ€ μ€ κ°μ₯ μμ μ¬μ μ©λ
int df = BlockingFlow(next, dest, min(c[curr][next] - f[curr][next], flow));
if (df > 0) { // μ¦κ° κ²½λ‘κ° μλ€λ©΄ df μ λμ νλ¦¬κ³ μ’
λ£
f[curr][next] += df;
f[next][curr] -= df;
return df;
}
}
}
return 0; // μ¦κ° κ²½λ‘λ₯Ό μ°Ύμ§ λͺ»ν¨
}
int Dinic()
{
int total = 0;
while (LevelGraph()) { // μ±ν¬μ λλ¬ν μ μλ€λ©΄ μ’
λ£
fill(work, work + MAX_V, 0);
while (1) {
int flow = BlockingFlow(S, E, INF);
if (flow == 0)
break; // λ μ΄μ μ°¨λ¨ μ λμ΄ μλ€λ©΄ μ’
λ£
total += flow;
}
}
return total;
}
int main()
{
int N;
cin >> N;
for (int i = 0; i < N; i++) {
int team;
cin >> team;
if (team == 1) {
c[S][i] = INF;
adj[S].push_back(i);
adj[i].push_back(S);
}
else if (team == 2) {
c[i][E] = INF;
adj[i].push_back(E);
adj[E].push_back(i);
}
}
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
cin >> c[i][j];
if (i != j)
adj[i].push_back(j);
}
}
cout << Dinic() << '\n';
// κ° μ§μμ μν μ¬λμ νλ¨νκΈ° μν΄ μμ€μμμ λλ¬ κ°λ₯μ± νλ³
bool visited[MAX_V]{ false };
visited[S] = true;
queue<int> Q;
Q.push(S);
while (!Q.empty()) {
int curr = Q.front();
Q.pop();
for (int next : adj[curr]) {
// μ¬μ μ©λμ΄ λ¨μμλ κ°μ λ§μ μ΄μ©
if (!visited[next] && c[curr][next] - f[curr][next] > 0) {
visited[next] = true;
Q.push(next);
}
}
}
// Aμ§μμ μν μ¬λλ€: μμ€μμ λλ¬ κ°λ₯
for (int i = 0; i < N; i++)
if (visited[i])
cout << i + 1 << ' ';
cout << '\n';
// Bμ§μμ μν μ¬λλ€: μμ€μμ λλ¬ λΆκ°λ₯
for (int i = 0; i < N; i++)
if (!visited[i])
cout << i + 1 << ' ';
cout << '\n';
}
4. νΈνν¬λ‘ννΈ μΉ΄ν μκ³ λ¦¬μ¦
1) λ¬Έμ
https://www.acmicpc.net/problem/3736
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
const int MAX = 10000;
const int INF = 1000000000;
int N;
// A[i], B[i]: κ·Έλ£Ήμ iλ² μ μ κ³Ό 맀μΉλ μλνΈ κ·Έλ£Ή μ μ λ²νΈ
// dist[i]: (Aκ·Έλ£Ήμ) iλ² μ μ μ λ 벨(?)
// used: (Aκ·Έλ£Ήμ) μ΄ μ μ μ΄ λ§€μΉμ μν΄ μλκ°?
int A[MAX], B[MAX], dist[MAX];
bool used[MAX];
vector<int> adj[MAX];
void bfs()
{
queue<int> Q;
for (int i = 0; i < N; i++) {
// 맀μΉμ μ μν Aκ·Έλ£Ήμ μ μ λ§ λ 벨 0μΈ μ±λ‘ μμ
if (!used[i]) {
dist[i] = 0;
Q.push(i);
}
else
dist[i] = INF;
}
// Aκ·Έλ£Ή μ μ μ 0, 1, 2, 3, ... μ λ 벨μ 맀κΉ
while (!Q.empty()) {
int a = Q.front();
Q.pop();
for (int b : adj[a]) {
if (B[b] != -1 && dist[B[b]] == INF) {
dist[B[b]] = dist[a] + 1;
Q.push(B[b]);
}
}
}
}
// μ΄λΆ λ§€μΉ μκ³ λ¦¬μ¦
bool dfs(int a)
{
for (int b : adj[a]) {
// μ΄λΆ 맀μΉκ³Ό μ°¨μ΄μ : dist λ°°μ΄μ λν μ‘°κ±΄μ΄ μΆκ°λ‘ λΆμ
if (B[b] == -1 || dist[B[b]] == dist[a] + 1 && dfs(B[b])) {
used[a] = true;
A[a] = b;
B[b] = a;
return true;
}
}
return false;
}
int main()
{
while (cin >> N) {
// Init
fill(A, A + MAX, -1);
fill(B, B + MAX, -1);
fill(used, used + MAX, false);
for (int i = 0; i < N; i++)
adj[i].clear();
// Input
for (int i = 0; i < N; i++) {
int job, server, M;
scanf("%d: (%d)", &job, &M);
for (int j = 0; j < M; j++) {
cin >> server;
adj[job].push_back(server - N);
}
}
// νΈνν¬λ‘ννΈ μΉ΄ν μκ³ λ¦¬μ¦
int answer = 0;
while (1) {
// κ° μ μ μ λ 벨μ λ§€κΈ°κ³ μμ
bfs();
// μ΄λΆ λ§€μΉ μκ³ λ¦¬μ¦μ μ΄μ©ν΄ μ¦κ° κ²½λ‘ μ°ΎκΈ°
int flow = 0;
for (int i = 0; i < N; i++)
if (!used[i] && dfs(i))
flow++;
answer += flow;
// λ μ΄μ μ¦κ° κ²½λ‘λ₯Ό λͺ» μ°ΎμΌλ©΄ μκ³ λ¦¬μ¦ μ’
λ£
if (flow == 0)
break;
}
cout << answer << '\n';
}
}
μ΅μ λΉμ© μ΅λ μ λ(MCMF, Min-Cost Max-Flow)
μ΅λ μ λμ λ°μμν€λλ°, μ΅μ λΉμ©μ ν΅ν΄ νλ €μΌ νλ€.
- κ·Έλνμ κ°μ μ λ λ€λ₯Έ κ°μ€μΉμΈ λΉμ©(Cost)μ΄ μ‘΄μ¬νλ€.
- λΉμ©μ κ°μ λ§λ€ λ€λ₯΄λ©° λΉμ©μ΄ dμ΄κ³ fλ§νΌμ μ λμ΄ νλ₯΄κ³ μμ λ, μλΉλλ λΉμ©μ d*fμ΄λ€.
- κ²½λ‘ λΉμ©μ μ§λκ° λͺ¨λ κ°μ λΉμ©μ ν©μ΄λ€.
μ리λ μ΅λ μ λ μκ³ λ¦¬μ¦κ³Ό λΉμ·νλ€. κ²½λ‘λ₯Ό μ°Ύμ μ λμ νλ¦¬κ³ μ΄ κ³Όμ μ λ μ΄μ κ²½λ‘κ° μμ λκΉμ§ λ°λ³΅νλ κ²μΈλ°, μ΄λ μ°Ύλ κ²½λ‘κ° μ΅λ¨ κ²½λ‘μ΄λ€. μ¦, κ°μ λΉμ©μ κΈ°μ€μΌλ‘ μ΅λ¨ κ²½λ‘λ₯Ό μ°ΎμΌλ©΄ λλ€.
κ·Έλ°λ°, μ λ κ·Έλνμμλ μλ°©ν₯ κ°μ λ μ‘΄μ¬νκΈ° λλ¬Έμ μμ κ°μ€μΉμμλ μ λμν΄μΌ νλ€. μλ°©ν₯ κ°μ μΌλ‘ μ λμ ν리면 κ·Έλ§νΌ μ λκ³Ό λΉμ©μ΄ λͺ¨λ μ€μ΄λ λ€.
1. SPFA(Shortest Path Faster Algorithm)
1) λ¬Έμ
https://www.acmicpc.net/problem/11408
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
vector<int> v[802];
int s = 0, t = 801;
int ca[802][802], fl[802][802], cost[802][802];
int main()
{
int n, m;
cin >> n >> m;
// Sourceμ μ¬λ μλ°©ν₯ μ°κ²°
for (int i = 1; i <= n; i++) {
v[s].push_back(i);
v[i].push_back(s);
ca[s][i] = 1; // μ©λμ μλ°©ν₯λ§ μΆκ°
}
// μΌκ³Ό Sink μλ°©ν₯ μ°κ²°
for (int i = 1; i <= m; i++) {
v[t].push_back(i + 400);
v[i + 400].push_back(t);
ca[i + 400][t] = 1; // μ©λμ μλ°©ν₯λ§ μΆκ°
}
// μ¬λκ³Ό μΌ μ°κ²°
for (int i = 1; i <= n; i++) {
int cnt;
cin >> cnt;
for (int j = 0; j < cnt; j++) {
int num, money;
cin >> num >> money;
// μ¬λκ³Ό μΌ μλ°©ν₯ μ°κ²°
v[i].push_back(num + 400);
v[num + 400].push_back(i);
// μ©λμ μλ°©ν₯λ§ μΆκ°
ca[i][num + 400] = 1;
// λΉμ©μ μλ°©ν₯
cost[i][num + 400] = money;
cost[num + 400][i] = -money;
}
}
// MCMF
int ans = 0, result = 0;
while (1) {
int pre[802]; // μ΄μ λ
Έλ μ μ₯
int dist[802]; // sλ‘λΆν° μ΅λ¨κ±°λ¦¬
fill(dist, dist + 802, 2e9);
fill(pre, pre + 802, -1);
queue<int> q;
bool visit[802]{};
q.push(s);
visit[s] = true;
dist[s] = 0;
while (!q.empty()) {
int node = q.front(); q.pop();
visit[node] = false;
for (auto next : v[node]) {
if (dist[next] > dist[node] + cost[node][next] && ca[node][next] > fl[node][next]) {
dist[next] = dist[node] + cost[node][next];
pre[next] = node;
if (!visit[next]) {
visit[next] = true;
q.push(next);
}
}
}
}
// μ΅λ¨ κ²½λ‘κ° λμ΄μ μμ
if (pre[t] == -1)
break;
int flow = 2e9;
for (int i = t; i != s; i = pre[i]) {
flow = min(flow, ca[pre[i]][i] - fl[pre[i]][i]);
}
for (int i = t; i != s; i = pre[i]) {
result += cost[pre[i]][i];
fl[pre[i]][i] += flow;
fl[i][pre[i]] -= flow;
}
ans += flow;
}
cout << ans << "\n" << result << "\n";
}