网络流模板与经典模型

网络流模板与经典模型

1.模板

dinic模板(常规的最大流模板,算法效率能满足大部分题)
#include<iostream>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
const ll N=10007;
const ll M=200007;
const ll inf=1e18;
ll n,m,S,T;
ll h[N],e[M],f[M],ne[M],tot;
ll d[N],cur[N];
queue<ll>sa;
void add(ll u,ll v,ll z){
	e[tot]=v;
	f[tot]=z;
	ne[tot]=h[u];
	h[u]=tot++;
	e[tot]=u;
	f[tot]=0;
	ne[tot]=h[v];
	h[v]=tot++;
}
bool bfs(){
	memset(d,-1,sizeof(d));
	while(!sa.empty())sa.pop();
	sa.push(S);
	d[S]=0;
	cur[S]=h[S];
	while(!sa.empty()){
		ll p=sa.front();
		sa.pop();
		for(int i=h[p];~i;i=ne[i]){
			ll to=e[i];
			if(d[to]==-1&&f[i]){
				d[to]=d[p]+1;
				cur[to]=h[to];
				if(to==T){
					return true;
				}
				sa.push(to);
			}
		}
	}
	return false;
}
ll dfs(ll p,ll now){
	if(p==T)return now;
	ll sum=0;
	for(int i=cur[p];~i&&sum<now;i=ne[i]){
		cur[p]=i;
		ll to=e[i];
		if(d[to]==d[p]+1&&f[i]){
			ll lin=dfs(to,min(f[i],now-sum));
			if(lin==0){
				d[to]=-1;
			}
			else{
				f[i]-=lin;
				f[i^1]+=lin;
				sum+=lin;
			}
		}
	}
	return sum;
}
ll dinic(){
	ll sum=0;
	while(bfs()){
		sum+=dfs(S,inf);
	}
	return sum;
}
int main(){
    scanf("%lld%lld%lld%lld", &n, &m, &S, &T);
    memset(h, -1, sizeof h);
    while(m--){
        ll u, v, z;
        scanf("%lld%lld%lld",&u,&v,&z);
        add(u,v,z);
    }
    printf("%lld
", dinic());
    return 0;
}
ISAP模板(比dinic更快,写题超时不妨试试这种)
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=100050;
const int maxm=200050;
const int inf=1e9;
struct edge {
    int to,w,next;
}e[maxm<<2];
int n,m;
int head[maxn],cnt=1,gap[maxn],last[maxn],d[maxn],que[maxn],ql,qr;
void init(){
  cnt=1;
  memset(head,0,sizeof(head));
}
void add(int u,int to,int w) {
    e[++cnt].to=to;
    e[cnt].w=w;
    e[cnt].next=head[u];
    head[u]=cnt;
    e[++cnt].to=u;
    e[cnt].w=0;
    e[cnt].next=head[to];
    head[to]=cnt;
}
int aug(int x,int s,int t,int mi) {
    if(x==t)return mi;
    int flow=0;
    for(int &i=last[x],v=e[i].to;i;i=e[i].next,v=e[i].to){
      if(d[x]==d[v]+1){
        int tmp=aug(v,s,t,min(mi,e[i].w));
        flow+=tmp;
        mi-=tmp;
        e[i].w-=tmp;
        e[i^1].w+=tmp;
        if(!mi)return flow;
      }
    }
    if(!(--gap[d[x]]))d[s]=t+1;
    ++gap[++d[x]],last[x]=head[x];
    return flow;
}
int dinic(int s,int t){
    fill(gap,gap+t+1,0);
    fill(d,d+t+1,0);
    ++gap[d[t]=1];
    for(int i=1;i<=t;++i)last[i]=head[i];
    que[ql=qr=1]=t;
    while (ql<=qr) {
        int x=que[ql++];
        for(int i=head[x],v=e[i].to;i;i=e[i].next,v=e[i].to){
          if(!d[v]){
            ++gap[d[v]=d[x]+1];
            que[++qr]=v;
          }
        }
    }
    int ans=aug(s,s,t,inf);
    while(d[s]<=t)ans+=aug(s,s,t,inf);
    return ans;
}
最小费最大流
#
include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N=5007;
const ll inf=1e18;
#define PII pair <ll,ll> 
#define fr first
#define sc second
struct madoka{
	vector<PII>ld;
	struct edge{
		ll v,f,c,r;
	};
	ll V=0,h[N],dis[N],pv[N],pe[N];
	vector<edge>G[N];
	inline void init(ll n){
		for(ll i=0;i<=V;i++){
			G[i].clear();
		}
		V=n;
		ld.clear();
	}
	inline void add(ll u,ll v,ll f,ll c){
		G[u].push_back({v,f,c,(ll)G[v].size()});
		G[v].push_back({u,0,-c,(ll)G[u].size()-1});
	}
	PII dinic(ll s,ll t,ll Flow){
		ll cost=0,flow=0,newflow;
		fill(h,h+1+V,0);
		while(Flow){
			priority_queue<PII,vector<PII>,greater<PII> >Q;
			fill(dis,dis+V+1,inf);
			dis[s]=0,Q.push({0,s});
			while(!Q.empty()){
				PII now=Q.top();Q.pop();
				ll u=now.sc;
				if(dis[u]<now.fr) continue;
				for(ll i=0;i<G[u].size();i++){
					edge &e=G[u][i];
					if(e.f>0&&dis[e.v]>dis[u]+e.c+h[u]-h[e.v]){
						dis[e.v]=dis[u]+e.c+h[u]-h[e.v];
						pv[e.v]=u,pe[e.v]=i;
						Q.push(PII(dis[e.v],e.v));
					}
				}
			}
			if(dis[t]==inf) break;
			for(ll i=0;i<=V;i++) h[i]+=dis[i];
			newflow=Flow;
			for(ll x=t;x!=s;x=pv[x]) newflow=min(newflow,G[pv[x]][pe[x]].f);
			Flow-=newflow,flow+=newflow,cost+=newflow*h[t];
			for(ll x=t;x!=s;x=pv[x]){
				edge &e=G[pv[x]][pe[x]];
				e.f-=newflow;
				G[x][e.r].f+=newflow;
			}
		}
		return {flow,cost};
	}
}A;
int main(){
	ll n,m;
	while(~scanf("%lld%lld",&n,&m)){
		ll s=1,t=n;
		A.init(n);
		for(ll i=1;i<=m;i++){
			ll u,v,c;
			scanf("%lld%lld%lld",&u,&v,&c);
			A.add(u,v,1,c);
		}
		PII Ans=A.dinic(s,t,inf);
	}
}

2.常用模型

(1)二分图上的最大流

飞行员配对

简单,建超级源点与超级汇点,连上边,边权容量为1,很明显最大匹配数就是最大流。

(2)多源汇最大流

多源汇最大流

简单,建超级源点与超级汇点,分别所有源点,与汇点,边权无限,跑最大流。

(3)拆点

餐饮

中等,当点上有点权控制流量时,而跑普通的最大流无法满足点权的限制,此时将点视为边,即将点拆分成两点,增加一条边出来控制流量,比较考验建图能力。

(4)最小割

模板

中等,一张图的最小割等于其最大流,很多题目较抽象,无法理解成求最大流,但能转换成最小割,便可通过求最大流算出最小割。、

(5)上下界的网络

困难,抽象难想,不好建模。

上下界可行流

将所有边分成两条,容量分别是下界,上界-下界。

下界的边是必须满足的,故将下界边的出点与汇点连,入点与源点连。

源点可以提供无限流量,让所有下界都充满水,检查汇点,若汇点流出的值小于所有下界的和,那么说明下界管子没流满水,就无可行流,反之合法。

有源汇上下界最大流

加汇点连一条去源点的边,便变成了无向图,按上方法求可行流,在求源汇最大流,即在满足可行流的情况下求最大流,注意去除新源汇的反边流量防止其退流,使其变成非可行流。

有源汇上下界最小流

和上题一样,最后只要求汇点到源点的最大流,看能退多少流回去就行。

(6)最大权闭合子图

最大获利

困难,公式推导太抽象,这里只说我的理解。

将负权点连向汇点,容量为点权*(-1)。

将正权点连向源点,容量为点权。

正负点权间边,容量无限。

考虑最小割,很明显只会切源点与正点的连线,或汇点与负点的连线。

首先,先假设正权的点我全要,此时我便要支付必须要选的负点的费用。

而最小割,若切在负汇边上,说明我支付了该负点的费用。

若切在正源边上,说明要获取此正点太亏,把正点权还回去实现反悔。

所以答案就所有正点权和减去最小割。

原文地址:https://www.cnblogs.com/whitelily/p/14522912.html