Dinic Algorithm

최대 유량을 구하는 네트워크 플로우 알고리즘인 Ford_Fulkerson 알고리즘과 Edmond Karp알고리즘을 소개해 드렸습니다. 네크워크 플로우 알고리즘 1

오늘은 동일한 역할을 하는 알고리즘이지만 시간복잡도가 더 좋은 Dinic 알고리즘을 소개하도록 하겠습니다.

Dinic의 전체적인 생김새는 F.F와 비슷합니다. 일단 대략적인 개관을 보도록 하겠습니다.

  1. bfs를 이용하여 레벨 그래프를 만듭니다. capacity가 0 이하면 흐를 수 없음에 유의해 주어야 합니다.
  2. 만들어진 레벨 그래프를 기반으로 플로우를 흘려줍니다. 현재 노드에서 가장 마지막으로 방문한 점을 저장하는 배열을 하나 만들어 흘렸던 유량을 다시 확인하지 않도록 합니다.
  3. 1,2의 과정을 레벨 그래프를 만들 수 없을 때 까지(synk까지 연결이 되지 않을 때 까지) 반복합니다.

코드 세부 내용을 설명하자면, 기존의 일반적인 그래프 문제에는 그래프와 capacity를 아래 코드처럼 선언했었습니다.

vector<int> graph[SZ];
int cap[SZ][SZ];

하지만 네트워크 플로우 알고리즘에서는 정점의 개수가 $ 10^4 $ 이상인 경우도 충분히 존재할 수 있습니다. 따라서 저는 Edge라는 구조체를 하나 선언하여 간선과 capacity에 대한 정보를 저장하도록 하겠습니다.

Dinic 알고리즘을 이용한 열혈강호 2 문제의 코드입니다.

#include<bits/stdc++.h>
#define pb push_back
#define INF (int)1e9
using namespace std;
struct edge{
	int to,cap,rev;
	edge(int to, int cap, int rev){
		this->to=to;		// 어디로 흐르는가
		this->cap=cap;
		this->rev=rev;	// 역간선이 저장되어 있는 index number 저장
	}
};
struct Dinic{
	int n,m,src,dst;
	vector<vector<edge>> graph;
	vector<int> crt;
	vector<int> lv;
	Dinic(int n, int m, int src, int dst){
		this->n=n;
		this->m=m;
		this->src=src;
		this->dst=dst;
		graph.resize(n+m+5);
		crt.resize(n+m+5);
		lv.resize(n+m+5);
	}
	void push_edge(int a, int b, int capa){
		graph[a].pb(edge(b,capa,graph[b].size()));
		graph[b].pb(edge(a,0,graph[a].size()-1));
	}
	bool bfs(){
		for(int i=0;i<=n+m+1;i++) lv[i]=0;
		queue<int> q;
		q.push(src); lv[src]=1;
		while(!q.empty()){
			int f=q.front(); q.pop();
			for(auto nxt:graph[f]){
				if(lv[nxt.to] || nxt.cap<=0) continue;
				q.push(nxt.to); lv[nxt.to]=lv[f]+1;
			}
		}
		return lv[dst]!=0;
	}
	int dfs(int x, int mnc){
		if(x==dst) return mnc;
		for(int &i=crt[x];i<(int)graph[x].size();i++){
			auto &e=graph[x][i];
			if(lv[x]>=lv[e.to] || e.cap<=0) continue;
			int f=dfs(e.to,min(mnc,e.cap));
			if(f>0){
				e.cap-=f;
				graph[e.to][e.rev].cap+=f;
				return f;
			}
		}
		return 0;
	}
	int flow(){
		int ans=0;
		while(bfs()){
			for(int i=0;i<=n+m+1;i++) crt[i]=0;
			int f;
			while((f=dfs(src,INF)) > 0) {
				ans+=f;
			}
		}
		return ans;
	}
};
int main(void){
	ios::sync_with_stdio(false); cin.tie(NULL);
	int n,m; cin>>n>>m;
	Dinic dn=Dinic(n,m,0,n+m+1);
	for(int i=1;i<=n;i++) dn.push_edge(dn.src,i,2);
	for(int i=n+1;i<=n+m;i++) dn.push_edge(i,dn.dst,1);
	for(int i=1;i<=n;i++){
		int x; cin>>x;
		for(int j=0;j<x;j++){
			int y; cin>>y;
			dn.push_edge(i,n+y,1);
		}
	}
	cout<<dn.flow();
	return 0;
}