网络流

网络流学习笔记

概念

流网络:原图(无反向边)\(G=(V,E)\),起点为s,终点为t。记 \(c(u,v)\)\(u\)\(v\) 的流量限制。

可行流:记 \(f(u,v)\)\(u\)\(v\) 之间的流量。则可行流 \(f\) 满足以下条件:

  • 容量限制:\(0 \leq f(u,v) \leq c(u,v)\)
  • 流量守恒:\(\sum f(v,x)=\sum f(x,u)\)

最大流:最大可行流

残量网络:和可行流对应。记为 \(G_f\)\(V_f=V,E_f=E和E中的所有反向边\)
\(c'(u,v) = \begin{cases} c(u,v)-f(u,v) & (u,v) \in E \\ f(v,u) & (v,u) \in E \end{cases}\)

增广路径:在残量网络里从起点能达到终点的一条路径。

割:把V分为两个集合S,T,满足\(S \cup T=V,S \cap T=\emptyset\)

  • 容量c(S,T):两个集合之间的容量之和。
  • 流量f(S,T):对于一种可行流,两个集合之间的流量之和。
  • 对于任意可行流f:\(|f|=f(u,v) \leq c(u,v)。\)
  • 最小割:最小割的容量
  • 最大流等于最小割

EK

在残量网络中寻找一条增广路,将流量尽可能地流入这条增广路。
重复以上步骤直到没有增广路。
时间复杂度:\(O(|V||E|^2)\),证明见OI Wiki

Dinic

对 EK 进行改进,我们想每次找多条增广路。为了避免形成环,找增广路前需要先对图进行分层,然后再在分层图中找增广路。
时间复杂度:\(O(|V|^2|E|)\),证明见OI Wiki
实际上会比理论上快很多。

int head[N], vi[M], wi[M], ne[M], tot = 1, dis[N], s, t;
queue <int> q;
bool bfs(){
	while(!q.empty())
		q.pop();
	memset(dis, -1, sizeof(dis));
	q.push(s);
	dis[s] = 0;
	while(!q.empty()){
		int x = q.front();
		q.pop();
		for(int i = head[x]; i; i = ne[i]){
			int y = vi[i];
			if(dis[y] == -1 && wi[i]){
				dis[y] = dis[x] + 1;
				if(y == t)
					return 1;
				q.push(y);
			}
		}
	}
	return 0;
}
long long dfs(int x, long long lim){
	if(x == t)
		return lim;
	long long flow = 0;
	for(int &i = cur[x]; i; i = ne[i]){
		int y = vi[i];
		if(dis[y] != dis[x] + 1 || !wi[i])
			continue;
		long long t = dfs(y, min(1ll * wi[i], lim - flow));
		if(!t)
			dis[y] = -1;
		wi[i] -= t, wi[i ^ 1] += t, flow += t;
		if(flow == lim)
		    break;
	}
	return flow;
}
long long dinic(){
	long long sum = 0, flow;
	while(bfs()){
		memcpy(cur, head, sizeof(head));
		while(flow = dfs(s, INF))
			sum += flow;
	}
	return sum;
}

费用流

int head[N], vi[M], wi[M], pr[M], ne[M], tot = 1;
int ss, tt, dis[N], cur[N];
bool inq[N], vis[N];
queue <int> q;
bool spfa(){
	memset(dis, 0x3f, sizeof(dis));
	memset(inq, 0, sizeof(inq));
	while(!q.empty())
		q.pop();
	dis[ss] = 0, inq[ss] = 1;
	q.push(ss);
	while(!q.empty()){
		int x = q.front();
		q.pop();
		inq[x] = 0;
		for(int i = head[x]; i; i = ne[i]){
			int y = vi[i];
			if(dis[y] > dis[x] + pr[i] && wi[i]){
				dis[y] = dis[x] + pr[i];
				if(!inq[y]){
					q.push(y);
					inq[y] = 1;
				}
			}
		}
	}
	return dis[tt] != dis[0];
}
int dfs(int x, int lim, int &cst){
	if(x == tt)
		return lim;
	vis[x] = 1;
	int flow = 0;
	for(int &i = cur[x]; i; i = ne[i]){
		int y = vi[i];
		if(dis[y] != dis[x] + pr[i] || !wi[i] || vis[y])
			continue;
		int dlt = dfs(y, min(lim - flow, wi[i]), cst);
		flow += dlt, cst += pr[i] * dlt, wi[i] -= dlt, wi[i ^ 1] += dlt;
		if(flow == lim)
			break;
	}
	vis[x] = 0;
	return flow;
}
int dinic(){
	int sum = 0, flow, cst = 0;
	while(spfa()){
		memcpy(cur, head, sizeof(head));
		while((flow = dfs(ss, INF, cst)))
			sum += flow;
	}
	return cst;
}
posted @ 2024-03-31 17:32  louisliang  阅读(26)  评论(0)    收藏  举报