【文文殿下】网络流学习笔记

最大流算法

Dinic

一个网络的割:存在一个边集,删去集合里的边时,S-T不再连通

最小割

所有割中边权之和最小的

最小割-最大流定理

最小割等于最大流

二分图匹配

(M)为边集(E)的一个子集,如果对于任何一个点,都最多被(M)中一条边覆盖,则成(M)为一个匹配。

最大匹配

使(|M|)最大的一个匹配(可能有多个)

最大匹配求法

源点S向所有X连边,Y向T连边,容量均为1,原始图上边容量均为1,最大流=最大匹配。

二分图最小顶点覆盖

(V')是点集(V)的一个子集,对于任意一条边,至少有一端在(V')内。

最小的一个(V')被称作最小顶点覆盖

最大匹配=最小顶点覆盖

证明:若有一条边未覆盖,则匹配+1,与最大匹配矛盾

二分图最大独立集

(V')(G)的一个独立集,当且仅当对于任何一条边,它至少有一端不在(V')

最大独立集大小等于(|V|-)最大匹配

证明:最小割。若(S,x)为割边,则在(V')中删去x。y同理。

有向图最小路径覆盖

用尽可能少的简单路径覆盖整张图。特殊的:一个点也是一条简单路径。

二分图建模:把每个点(x)拆成(x_1,x_2),分别属于X,Y.

对原图一条边,(x,y),链接((x_1,y_2))

易证:最小路径覆盖=(|V|-)最大匹配。

X中未被匹配的点是每条路径的起点

二分图点权最大独立集

最大权独立集=(sum value(x) -)最小权顶点覆盖

最小权覆盖=最大匹配=最小割=最大流(原图的边流量为无穷大)

建图:S向所有X连边,容量为value(x),所有的Y向T连边,容量为value(y) ,原图中的边容量为无穷大

最大权闭合子图

(U)(G)的一个子图,x属于(U) 当且仅当对于任何一条边(x,y),y也属于(U),那么(U)(G)的一个闭合子图

权值和最大的闭合子图称为最大权闭合子图(权值有正有负)

求法:建立源点S,向所有x(value(x)>0)连边,容量为value(x),建立汇点T,所有y(value(y)<0)向T连边,容量为-value(y),对于原图中的边(x,y),在x,y之间连一条容量为无穷大的边。

最大权闭合子图=(sum_{value(x)>0} value(x) -) 最小割

证明:

若选了一个点x,则他的后继一定会选(最大流),满足闭合子图。

若一个(S,x)为割边,说明(S,x)满流,则选了x,他的后继付出的代价一定不小于x的权值,即x的贡献小于等于0

有节点容量的最大流

建图:对于每一个点x拆成两个点(x_1,x_2),对于所有入边,链接(x_1)出边从(x_2)出,(x_1,x_2)之间链接一条容量为节点容量的边

上下界网络流

每条边有流量上界和流量下界

建图:先建新源点S1,新汇点T1,对于一条边(x,y) ,从x到y连一条(c(e)-l(e))的边,从S1向y连一条(l(e))的边,从x向T1连一条(l(e))的边。

建一条从T到S容量为k的边。

存在可行流:从S1出发的所有边都满流。

最小流:二分K,存在可行流,最小的K即为最小流

最大流:先跑可行流。跑完以后,把自己加入的必要弧删去,再恢复源汇,尝试增广S-T

费用流

算法:ZKW费用流

板子

最大流(BZOJ1694)

#include<cstdio>
#include<cstring>
#include<queue>
const int maxn = 1010;
const int maxm = 2e5+10;
const int inf = 0x3f3f3f3f;
int n,k,S,T,wet[maxm],nxt[maxm],to[maxm],hd[maxn],cnt=1,dis[maxn],cur[maxn];
std::queue<int> Q;
inline void add(int u,int v,int w) {
	 nxt[++cnt]=hd[u];
	 hd[u]=cnt;
	 to[cnt]=v;
	 wet[cnt]=w;
}
inline bool bfs(void) {
	for(int i = 0;i<=T;++i) cur[i]=hd[i];
	memset(dis,0,sizeof dis);
	dis[S]=1;
	Q.push(S);
	while(!Q.empty()) {
		int u = Q.front();
		Q.pop();
		for(int i = hd[u];i;i=nxt[i]){ 
			int nx = to[i];
			if(!dis[nx]&&wet[i]>0) {
				dis[nx]=dis[u]+1;
				Q.push(nx);
			}
		}
	}
	return dis[T]!=0;
}
inline int dfs(int u,int flow) {
	if(u==T) return flow;
	int tmp = 0;
	for(int &i = cur[u];i;i=nxt[i]) {
		int nx = to[i];
		if(dis[nx]==dis[u]+1&&wet[i]>0) {
			int ret = dfs(nx,std::min(flow,wet[i]));
			if(ret<0) ret=0;
			flow-=ret;
			tmp+=ret;
			wet[i]-=ret;
			wet[i^1]+=ret;
			if(flow==0) break;
		}
	}
	return tmp;
}
inline int dinic(void) {
	int flow = 0;
	while(bfs()) {
		flow+=dfs(S,inf);
	}
	return flow;
}
int main() {
	scanf("%d%d",&n,&k);
	for(int i = 0;i<k;++i) {
		 int x,y;
		 scanf("%d%d",&x,&y);
		 add(x,n+y,1);
		 add(n+y,x,0);
	}
	S = 0;
	T = n+n+1;
	for(int i = 1;i<=n;++i) {add(S,i,1);add(i,S,0);}
	for(int i = n+1;i<=n+n;++i) {add(i,T,1);add(T,i,0);}
	printf("%d",dinic());
	return 0;
}

费用流

#include<cstdio>
#include<cstring>
#include<queue>
typedef long long ll;
const int maxn = 5050,maxm=1e5+10;
const int inf = 0x3f3f3f3f;
int n,m,S,T,cnt=1,wet[maxm],fee[maxm],hd[maxn],nxt[maxm],to[maxm];
int dis[maxn],cur[maxn];
bool vis[maxn];
std::queue<int> Q;
inline void add(int u,int v,int w,int f) {
	nxt[++cnt]=hd[u];
	to[cnt]=v;
	wet[cnt]=w;
	fee[cnt]=f;
	hd[u]=cnt;
}
inline bool spfa(void) {
	memset(dis , 0x3f3f3f3f , sizeof dis);
	memset(vis, 0 , sizeof vis);
	for(int i = 1;i<=n;++i) cur[i]=hd[i];
	Q.push(S);
	dis[S]=0;
	while(!Q.empty()) {
		int u = Q.front();
		Q.pop();
		vis[u]=0;
		for(int i = hd[u];i;i=nxt[i]) {
			int nx = to[i];
			if(dis[nx]>dis[u]+fee[i]&&wet[i]) {
				dis[nx]=dis[u]+fee[i];
				if(!vis[nx]) {
					Q.push(nx);
					vis[nx]=1;
				}
			}
		}
	}
	return dis[T]!=dis[0];
}
inline int dfs(int u,int c,int &flow ,ll &ans) {
	vis[u]=1;
	 if(u==T) {
		 flow+=c;
		 ans+=dis[u]*c;
		 return c;
	 }
	 int tmp = 0;
	 for(int &i = cur[u];i;i=nxt[i]) {
		 int nx = to[i];
		 if(!vis[nx]&&wet[i]>0&&dis[nx]==dis[u]+fee[i]) {
			int t = dfs(nx,std::min(c,wet[i]),flow,ans);
			wet[i]-=t;
			wet[i^1]+=t;
			tmp+=t;
			c-=t;
		 }
	 }
	 return tmp;
}
inline void dinic(void) {
	 int flow = 0;
	 ll ans = 0;
	 while(spfa()) {
		 vis[T]=1;
		 while(vis[T]) {
			vis[T]=0;
		 	dfs(S,inf,flow,ans);
		 }
	 }
	 printf("%d %lld
",flow,ans);
	 return;
}
int main() {
	scanf("%d%d%d%d",&n,&m,&S,&T);
	for(int i = 0;i<m;++i) {
		int u,v,w,f;
		scanf("%d%d%d%d",&u,&v,&w,&f);
		add(u,v,w,f);
		add(v,u,0,-f);
	}
	dinic();
	return 0;
}
原文地址:https://www.cnblogs.com/Syameimaru/p/10117145.html