[C++] λ„€νŠΈμ›Œν¬ μœ λŸ‰(Network Flow)

2021. 11. 18. 17:28Β·πŸ“ Computer Science/✏ Algorithm

λ„€νŠΈμ›Œν¬ μœ λŸ‰(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

 

μœ λŸ‰ 1(S-A)을 보낸 ν›„ λ‹€μ‹œ ν•œλ²ˆ μœ λŸ‰ 1(S-B)κ³Ό μœ λŸ‰ 4(S-C)λ₯Ό 보낸 ν›„ 결과이닀.

 

μœ λŸ‰ 1 + μœ λŸ‰1 + μœ λŸ‰4

 

μ§€κΈˆκΉŒμ§€ Sμ—μ„œ T둜 보낸 μœ λŸ‰μ˜ 합은 6인데, μ—¬κΈ°μ„œ 더 λ§Žμ€ μœ λŸ‰μ„ T둜 보낼 수 μžˆμ„κΉŒ? λΆˆκ°€λŠ₯ν•˜λ‹€. μ™œλƒν•˜λ©΄ κ²½λ‘œμ— μ†ν•œ κ°„μ„  (D, T)μ—” 더 이상 μš©λŸ‰μ΄ λ‚¨μ•„μžˆμ§€ μ•Šλ‹€.

 

더 λ§Žμ€ μœ λŸ‰μ„ λ³΄λ‚΄λŠ” 방법은 μ—†μ„κΉŒ?

 

μœ λŸ‰ 7

 

처음 κ²½λ‘œκ°€ μ•„λ‹Œ μ΅œλŒ€ μœ λŸ‰ μ•Œκ³ λ¦¬μ¦˜μ„ 따라 경둜λ₯Ό 찾으면 더 λ§Žμ€ μœ λŸ‰μ„ 보낼 수 있게 λœλ‹€.

 

1. ν¬λ“œ ν’€μ»€μŠ¨ μ•Œκ³ λ¦¬μ¦˜(Ford-Fulkerson algorithm)

DFS둜 κ΅¬ν˜„ν•œ μ•Œκ³ λ¦¬μ¦˜μœΌλ‘œ κΈ°μ΄ˆκ°€ λ˜λŠ” μ•Œκ³ λ¦¬μ¦˜μ΄λ‹€.

 

1) κΈ°λ³Έ 원리

  • Sμ—μ„œ T둜 κ°€λŠ” 증가 경둜 쀑 μ•„λ¬΄κ±°λ‚˜ ν•˜λ‚˜λ₯Ό μ°ΎλŠ”λ‹€.
  • 증가 경둜 쀑 차단 간선을 μ°ΎλŠ”λ‹€. 이 간선은 κ²½λ‘œμƒμ—μ„œ F(c(u, v) - f(u, v)) 값이 μ΅œμ†ŒμΈ 간선이닀.
  • 경둜 λ‚΄ λͺ¨λ“  간선에 F μœ λŸ‰μ„ μΆ”κ°€ν•œλ‹€. 즉, Sμ—μ„œ T둜 이 경둜λ₯Ό λ”°λΌμ„œ F만큼의 μœ λŸ‰μ„ μƒˆλ‘œ ν˜λ €λ³΄λ‚Έ 것이닀.
  • μ΄λ•Œ, μŒμ˜ μœ λŸ‰λ„ λ§Œμ‘±ν•΄μ•Ό ν•˜κΈ° λ•Œλ¬Έμ— μ—­λ°©ν–₯도 이동 κ°€λŠ₯ν•˜λ‹€.
  • μœ„ 과정을 더 이상 증가 κ²½λ‘œκ°€ 없을 λ•ŒκΉŒμ§€ λ°˜λ³΅ν•œλ‹€.

 

μœ λŸ‰ 3(S-A) + μœ λŸ‰ 2(S-B) + μœ λŸ‰4(S-C)
μ—­λ°©ν–₯ 경둜 μˆ˜ν–‰ - μœ λŸ‰ 3(S-A) + μœ λŸ‰ 2(S-B) + μœ λŸ‰4(S-C) + μœ λŸ‰ 1(S-C)

 

2) 문제

 

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

 

6086번: μ΅œλŒ€ μœ λŸ‰

첫째 쀄에 μ •μˆ˜ N (1 ≤ N ≤ 700)이 μ£Όμ–΄μ§„λ‹€. λ‘˜μ§Έ 쀄뢀터 N+1번째 μ€„κΉŒμ§€ νŒŒμ΄ν”„μ˜ 정보가 μ£Όμ–΄μ§„λ‹€. 첫 번째, 두 번째 μœ„μΉ˜μ— νŒŒμ΄ν”„μ˜ 이름(μ•ŒνŒŒλ²³ λŒ€λ¬Έμž λ˜λŠ” μ†Œλ¬Έμž)이 μ£Όμ–΄μ§€κ³ , μ„Έ 번째 μœ„

www.acmicpc.net

#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

 

17412번: λ„μ‹œ μ™•λ³΅ν•˜κΈ° 1

첫째 쀄에 두 μ •μˆ˜ N(3 ≤ N ≤ 400), P(1 ≤ P ≤ 10,000)이 μ£Όμ–΄μ§„λ‹€. λ‹€μŒ P개의 μ€„μ—λŠ” 각 길이 μ—°κ²°ν•˜λŠ” 좜발 λ„μ‹œμ™€ 도착 λ„μ‹œμ˜ λ²ˆν˜Έκ°€ μ£Όμ–΄μ§€λ©°, 두 λ²ˆν˜ΈλŠ” λ‹€λ₯΄λ‹€.

www.acmicpc.net

#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 큰 μ •μ μœΌλ‘œ 이동이 κ°€λŠ₯ν•˜λ‹€.
    • 더 이상 μ†ŒμŠ€μ—μ„œ μ‹±ν¬λ‘œ 흘릴 수 μžˆλŠ” μœ λŸ‰μ΄ μ—†κ²Œ λ§Œλ“œλŠ” μœ λŸ‰ μƒνƒœλ₯Ό λ§ν•œλ‹€.

 

초기 κ·Έλž˜ν”„ 1
레벨 κ·Έλž˜ν”„ 1 - 차단 μœ λŸ‰ 14
초기 κ·Έλž˜ν”„ 2 - μœ λŸ‰ 14
레벨 κ·Έλž˜ν”„ 2 - 차단 μš©λŸ‰ 5
초기 κ·Έλž˜ν”„ 3 - μœ λŸ‰ 19
레벨 κ·Έλž˜ν”„ 3 - 차단 μš©λŸ‰ μ—†μŒ

 

2) 문제

 

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

 

13161번: λΆ„λ‹¨μ˜ μŠ¬ν””

첫 번째 μ€„μ—λŠ” UCPC κ΅¬μ„±μ›μ˜ 수 N(1 ≤ N ≤ 500)이 μ£Όμ–΄μ§„λ‹€. 두 번째 μ€„μ—λŠ” N개의 μ •μˆ˜κ°€ μ£Όμ–΄μ§€λŠ”λ°, i번째 μˆ˜κ°€ 1이면 i번 μ‚¬λžŒμ€ 무쑰건 Aμ§„μ˜μ— λ“€μ–΄κ°€μ•Ό 함을, 2라면 무쑰건 Bμ§„μ˜μ— λ“€μ–΄κ°€μ•Ό

www.acmicpc.net

#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

 

3736번: System Engineer

There are two data sets. In the first case, the number of jobs n is 2, numbered 0 and 1. The sequence of requests for job 0 is: 0: (1) 2, meaning that job 0 requires 1 sever, the server numbered 2. The sequence of requests for job 1 is: 1: (1) 2, meaning t

www.acmicpc.net

#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)

SPFA

 

1) 문제

 

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

 

11408번: μ—΄ν˜ˆκ°•ν˜Έ 5

κ°•ν˜Έλ„€ νšŒμ‚¬μ—λŠ” 직원이 Nλͺ…이 있고, ν•΄μ•Ό ν•  일이 Mκ°œκ°€ μžˆλ‹€. 직원은 1λ²ˆλΆ€ν„° Nλ²ˆκΉŒμ§€ λ²ˆν˜Έκ°€ 맀겨져 있고, 일은 1λ²ˆλΆ€ν„° Mλ²ˆκΉŒμ§€ λ²ˆν˜Έκ°€ 맀겨져 μžˆλ‹€. 각 직원은 ν•œ 개의 일만 ν•  수 있고, 각각

www.acmicpc.net

#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";
}
μ €μž‘μžν‘œμ‹œ (μƒˆμ°½μ—΄λ¦Ό)
'πŸ“ Computer Science/✏ Algorithm' μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ κΈ€
  • [C++] μ€‘κ°„μ—μ„œ λ§Œλ‚˜κΈ°(Meet In The Middle)
  • [C++] λ¬Έμžμ—΄
  • [C++] ν¬μ†Œ ν…Œμ΄λΈ”(Sparse Table)
  • [C++] μœ„μƒ μ •λ ¬(Topological Sort)
Blxxming
Blxxming
CS 지식과 κ³΅λΆ€ν•˜λ‹€ 배운 것, κ²½ν—˜ν•œ 것 등을 κΈ°λ‘ν•˜λŠ” λΈ”λ‘œκ·Έμž…λ‹ˆλ‹€.
  • Blxxming
    πŸ’‘λ²ˆλœ©πŸ’‘
    Blxxming
  • 전체
    였늘
    μ–΄μ œ
  • 곡지사항

    • Tech Interview
    • πŸ“š Tech (246)
      • πŸ“ Computer Science (96)
        • ✏ OS (12)
        • ✏ Network & Web (10)
        • ✏ Database (11)
        • ✏ Data Structure (6)
        • ✏ Algorithm (40)
        • ✏ Design Pattern (9)
        • ✏ Cloud Computing (3)
        • ✏ (5)
      • πŸ“ Language (73)
        • ✏ Language (6)
        • ✏ C & C++ (11)
        • ✏ C# (19)
        • ✏ JAVA (37)
      • πŸ“ Game (43)
        • ✏ Computer Graphics (2)
        • ✏ Unity (14)
        • ✏ Unreal (26)
        • ✏ (1)
      • πŸ“ Book (34)
        • ✏ Effective (3)
        • ✏ Game Server (16)
        • ✏ Clean Code (14)
        • ✏ (1)
  • hELLOΒ· Designed Byμ •μƒμš°.v4.10.0
Blxxming
[C++] λ„€νŠΈμ›Œν¬ μœ λŸ‰(Network Flow)
μƒλ‹¨μœΌλ‘œ

ν‹°μŠ€ν† λ¦¬νˆ΄λ°”