网络流

相关资料

用最通俗的语言让你学会网络流

EK不够快?再学个Dinic吧

究级的最大流算法:ISAP与HLPP

1. 最大流

基本概念:用最通俗的语言让你学会网络流

1.EK(Edmond—Karp)算法

思路十分暴力,就是建反边以便反悔,不断宽搜找可行流,然后暴力扩展,直到没有可行流为止。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<map>
#include<set>
using namespace std;
typedef long long LL;
const int MAXN=1e4+7,MAXM=1e5+7,INF=1e9;
struct Pre{
	int v/*vertex*/,e/*edge*/;
}pre[MAXN];
int N,M,S,T;
int head[MAXN],val[MAXM*2],to[MAXM*2],nxt[MAXM*2],top=2;
bool inq[MAXN];
inline void add(int x,int y,int z){
	nxt[top]=head[x];
	head[x]=top;
	to[top]=y;
	val[top]=z;
	top++; 
}
inline bool bfs(){
	queue<int> que;
	memset(inq,0,sizeof(inq));
	memset(pre,0,sizeof(pre));
	que.push(S);
	inq[S]=1;
	while(que.size()){
		int ii=que.front();
		que.pop();
		for(int i=head[ii];i;i=nxt[i]){
			if(!val[i]||inq[to[i]]){
				continue;
			}
			pre[to[i]].v=ii;
			pre[to[i]].e=i;
			if(to[i]==T){
				return 1;
			}
			inq[to[i]]=1;
			que.push(to[i]);
		}
	}
	return 0;
}
inline int EK(){
	int ans=0;
	while(bfs()){
		int minv=INF;
		for(int i=T;i!=S;i=pre[i].v){
			minv=min(minv,val[pre[i].e]);
		}
		for(int i=T;i!=S;i=pre[i].v){
			val[pre[i].e]-=minv;
			val[pre[i].e^1]+=minv;
		}
		ans+=minv;
	}
	return ans;
}
int main(){
	scanf("%d%d%d%d",&N,&M,&S,&T);
	for(int i=1;i<=M;i++){
		int ii,jj,kk;
		scanf("%d%d%d",&ii,&jj,&kk);
		add(ii,jj,kk);
		add(jj,ii,0);
	}
	printf("%d",EK());
	return 0;
}

2.Dinic算法

Dinic在EK的基础上又一定的改进,每次宽搜顺便分层,然后用深搜扩展所有可行流,如此往复。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<map>
#include<set>
using namespace std;
typedef long long LL;
const int MAXN=1e4+7,MAXM=1e5+7,INF=0x3f3f3f3f;
int N,M,S,T;
int head[MAXN],val[MAXM*2],to[MAXM*2],nxt[MAXM*2],top=2;
int inq[MAXN],de[MAXN];
inline void add(int x,int y,int z){
    nxt[top]=head[x];
    head[x]=top;
    to[top]=y;
    val[top]=z;
    top++;
}
inline bool bfs(){
    queue<int> que;
    memset(inq,0,sizeof(inq));
    memset(de,0x3f,sizeof(de));
    que.push(S);
    de[S]=0;
    while(que.size()){
        int ii=que.front();
        que.pop();
        inq[ii]=0;
        for(int i=head[ii];i;i=nxt[i]){
            if(val[i]&&de[to[i]]>de[ii]+1){
                de[to[i]]=de[ii]+1;
                if(!inq[to[i]]){
                    que.push(to[i]);
                    inq[to[i]]=1;
                }
            }
        }
    }
    return de[T]==INF?0:1;
}
int dfs(int x,int flow){
    if(x==T){
        return flow;
    }
    for(int i=head[x];i;i=nxt[i]){
        if(val[i]&&de[to[i]]==de[x]+1){
            int ii=dfs(to[i],min(flow,val[i]));
            if(ii){
                val[i]-=ii;
                val[i^1]+=ii;
                return ii;
            }
        }
    }
    return 0;
}
inline int Dinic(){
    int ans=0,ii;
    while(bfs()){
        do{
            ii=dfs(S,INF);
            ans+=ii;
        }while(ii);
    }
    return ans;
}
int main(){
    scanf("%d%d%d%d",&N,&M,&S,&T);
    for(int i=1;i<=M;i++){
        int ii,jj,kk;
        scanf("%d%d%d",&ii,&jj,&kk);
        add(ii,jj,kk);
        add(jj,ii,0);
    }
    printf("%d",Dinic());
    return 0;
}

另外,在Dinic的基础下,还可以进行当前弧优化,也就是说在同一次宽搜后,每一条边只访问一次,可以提升一定的效率

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<map>
#include<set>
using namespace std;
typedef long long LL;
const int MAXN=1e4+7,MAXM=1e5+7,INF=0x3f3f3f3f;
int N,M,S,T;
int head[MAXN],val[MAXM*2],to[MAXM*2],nxt[MAXM*2],top=2;
int inq[MAXN],de[MAXN],cur[MAXN];
inline void add(int x,int y,int z){
	nxt[top]=head[x];
	head[x]=top;
	to[top]=y;
	val[top]=z;
	top++;
}
inline bool bfs(){
	queue<int> que;
	memset(inq,0,sizeof(inq));
	memset(de,0x3f,sizeof(de));
	memcpy(cur,head,sizeof(cur));
	que.push(S);
	de[S]=0;
	while(que.size()){
		int ii=que.front();
		que.pop();
		inq[ii]=0;
		for(int i=head[ii];i;i=nxt[i]){
			if(val[i]&&de[to[i]]>de[ii]+1){
				de[to[i]]=de[ii]+1;
				if(!inq[to[i]]){
					que.push(to[i]);
					inq[to[i]]=1;
				}
			}
		}
	}
	return de[T]==INF?0:1;
}
int dfs(int x,int flow){
	if(x==T){
		return flow;
	}
	for(int i=cur[x];i;i=nxt[i]){
		cur[x]=i;
		if(val[i]&&de[to[i]]==de[x]+1){
			int ii=dfs(to[i],min(flow,val[i]));
			if(ii){
				val[i]-=ii;
				val[i^1]+=ii;
				return ii;
			}
		}
	}
	return 0;
}
inline int Dinic(){
	int ans=0,ii;
	while(bfs()){
		do{
			ii=dfs(S,INF);
			ans+=ii;
		}while(ii);
	}
	return ans;
}
int main(){
	scanf("%d%d%d%d",&N,&M,&S,&T);
	for(int i=1;i<=M;i++){
		int ii,jj,kk;
		scanf("%d%d%d",&ii,&jj,&kk);
		add(ii,jj,kk);
		add(jj,ii,0);
	}
	printf("%d",Dinic());
	return 0;
}

Dinic还可以加used优化,记录已经用过的总流量,防止找到过多流量。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<map>
#include<set>
using namespace std;
typedef long long LL;
const int MAXN=1e4+7,MAXM=1e5+7,INF=0x3f3f3f3f;
int N,M,S,T,maxflow;
int head[MAXN],val[MAXM*2],to[MAXM*2],nxt[MAXM*2],top=2;
int inq[MAXN],de[MAXN],cur[MAXN];
inline void add(int x,int y,int z){
	nxt[top]=head[x];
	head[x]=top;
	to[top]=y;
	val[top]=z;
	top++;
}
inline bool bfs(){
	queue<int> que;
	memset(inq,0,sizeof(inq));
	memset(de,0x3f,sizeof(de));
	memcpy(cur,head,sizeof(cur));
	que.push(S);
	de[S]=0;
	while(que.size()){
		int ii=que.front();
		que.pop();
		inq[ii]=0;
		for(int i=head[ii];i;i=nxt[i]){
			if(val[i]&&de[to[i]]>de[ii]+1){
				de[to[i]]=de[ii]+1;
				if(!inq[to[i]]){
					que.push(to[i]);
					inq[to[i]]=1;
				}
			}
		}
	}
	return de[T]==INF?0:1;
}
int dfs(int x,int flow){//·µ»ØÒÑÓÃÁ÷Á¿ 
	if(x==T){
		maxflow+=flow;
		return flow;
	}
	int used=0;
	for(int i=cur[x];i;i=nxt[i]){
		cur[x]=i;
		if(val[i]&&de[to[i]]==de[x]+1){
			int ii=dfs(to[i],min(flow-used,val[i]));//·ÀÖ¹³¬¹ýÁ÷Á¿ 
			if(ii){
				used+=ii;
				val[i]-=ii;
				val[i^1]+=ii;
				if(used==flow){//ÂúÁ÷Á¿ 
					break;
				}
			}
		}
	}
	return used;
}
inline void Dinic(){
	while(bfs()){
		dfs(S,INF);
	}
	printf("%d",maxflow);
}
int main(){
	scanf("%d%d%d%d",&N,&M,&S,&T);
	for(int i=1;i<=M;i++){
		int ii,jj,kk;
		scanf("%d%d%d",&ii,&jj,&kk);
		add(ii,jj,kk);
		add(jj,ii,0);
	}
	Dinic();
	return 0;
}

3. ISAP算法

ISAP在Dinic基础上优化,只做一次bfs,从T向S统计深度,当一个点的出流量大于入流量时将他的深度加一,出现断层时就结束。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<map>
#include<set>
using namespace std;
typedef long long LL;
const int MAXN=1e4+7,MAXM=1e5+7,INF=0x3f3f3f3f;
int N,M,S,T,maxflow;
int head[MAXN],val[MAXM*2],to[MAXM*2],nxt[MAXM*2],top=2;
int gap[MAXN],de[MAXN],cur[MAXN];
inline void add(int x,int y,int z){
	nxt[top]=head[x];
	head[x]=top;
	to[top]=y;
	val[top]=z;
	top++;
}
inline void bfs(){
	queue<int> que;
	memset(gap,0,sizeof(gap));
	memset(de,-1,sizeof(de));
	que.push(T);
	de[T]=0;
	gap[0]=1;
	while(que.size()){
		int ii=que.front();
		que.pop();
		for(int i=head[ii];i;i=nxt[i]){
			if(de[to[i]]!=-1){
				continue;
			}
			de[to[i]]=de[ii]+1;
			gap[de[to[i]]]++;
			que.push(to[i]);
		}
	}
}
int dfs(int x,int flow){
	if(x==T){
		maxflow+=flow;
		return flow;
	}
	int used=0;
	for(int i=cur[x];i;i=nxt[i]){
		cur[x]=i;
		if(val[i]&&de[to[i]]+1==de[x]){
			int ii=dfs(to[i],min(flow-used,val[i]));//·ÀÖ¹³¬¹ýÁ÷Á¿ 
			if(ii){
				used+=ii;
				val[i]-=ii;
				val[i^1]+=ii;
				if(used==flow){
					return used;
				}
			}
		}
	}
	gap[de[x]]--;
	if(!gap[de[x]]){
		de[S]=N+1;
	}
	de[x]++;
	gap[de[x]]++;
	return used;
}
inline void ISAP(){
	bfs();
	while(de[S]<N){
		memcpy(cur,head,sizeof(cur));
		dfs(S,INF);
	}
	printf("%d",maxflow);
}
int main(){
	scanf("%d%d%d%d",&N,&M,&S,&T);
	for(int i=1;i<=M;i++){
		int ii,jj,kk;
		scanf("%d%d%d",&ii,&jj,&kk);
		add(ii,jj,kk);
		add(jj,ii,0);
	}
	ISAP();
	return 0;
}

2.最小费用最大流

1. EK算法

将bfs改成spfa求最短路即可

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<map>
#include<set>
using namespace std;
typedef long long LL;
const int MAXN=1e4+7,MAXM=1e5+7,INF=0x3f3f3f3f;
struct Pre{
	int e,v;
}pre[MAXN];
int N,M,S,T;
int head[MAXN],to[MAXM*2],val[MAXM*2],cost[MAXM*2],nxt[MAXM*2],tp=2;
int dis[MAXN];
bool inq[MAXN];
inline void add(int x,int y,int z,int k){
	nxt[tp]=head[x];
	head[x]=tp;
	to[tp]=y;
	val[tp]=z;
	cost[tp]=k;
	tp++;
}
inline bool spfa(){
	queue<int> que;
	memset(pre,0,sizeof(pre));
	memset(dis,0x3f,sizeof(dis));
	memset(inq,0,sizeof(inq));
	inq[S]=1;
	dis[S]=0;
	que.push(S);
	while(que.size()){
		int ii=que.front();
		que.pop();
		inq[ii]=0;
		for(int i=head[ii];i;i=nxt[i]){
			if(val[i]&&dis[to[i]]>dis[ii]+cost[i]){
				dis[to[i]]=dis[ii]+cost[i];
				pre[to[i]].v=ii;
				pre[to[i]].e=i;
				if(!inq[to[i]]){
					inq[to[i]]=1;
					que.push(to[i]);
				}
			}
		}
	}
	return dis[T]==INF?0:1;
}
inline void EK(){
	int ans=0,tot=0;
	while(spfa()){
		int minv=INF;
		for(int i=T;i!=S;i=pre[i].v){
			minv=min(minv,val[pre[i].e]);
		}
		for(int i=T;i!=S;i=pre[i].v){
			val[pre[i].e]-=minv;
			val[pre[i].e^1]+=minv;
		}
		ans+=minv;
		tot+=minv*dis[T];
	}
	printf("%d %d",ans,tot);
}
int main(){
	scanf("%d%d%d%d",&N,&M,&S,&T);
	for(int i=1;i<=M;i++){
		int ii,jj,kk,ll;
		scanf("%d%d%d%d",&ii,&jj,&kk,&ll);
		add(ii,jj,kk,ll);
		add(jj,ii,0,-ll);
	}
	EK();
	return 0;
}

2. Dinic

和Dinic一样,但不可以当前弧优化

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<map>
#include<set>
using namespace std;
typedef long long LL;
const int MAXN=1e4+7,MAXM=1e5+7,INF=0x3f3f3f3f;
int N,M,S,T,maxflow,totcost;
int head[MAXN],to[MAXM*2],val[MAXM*2],cost[MAXM*2],nxt[MAXM*2],tp=2;
int vis[MAXN],dis[MAXN];
inline void add(int x,int y,int z,int k){
	nxt[tp]=head[x];
	head[x]=tp;
	to[tp]=y;
	val[tp]=z;
	cost[tp]=k;
	tp++;
}
inline bool spfa(){
	queue<int> que;
	memset(vis,0,sizeof(vis));
	memset(dis,0x3f,sizeof(dis));
	que.push(S);
	vis[S]=1;
	dis[S]=0;
	while(que.size()){
		int ii=que.front();
		que.pop();
		vis[ii]=0;
		for(int i=head[ii];i;i=nxt[i]){
			if(val[i]&&dis[to[i]]>dis[ii]+cost[i]){
				dis[to[i]]=dis[ii]+cost[i];
				if(!vis[to[i]]){
					que.push(to[i]);
					vis[to[i]]=1;
				}
			}
		}
	}
	return dis[T]==INF?0:1;
}
int dfs(int x,int flow){
	if(x==T){
		maxflow+=flow;
		return flow;
	}
	int used=0;
	vis[x]=1;
	for(int i=head[x];i;i=nxt[i]){
		if(!vis[to[i]]&&val[i]&&dis[to[i]]==dis[x]+cost[i]){
			int ii=dfs(to[i],min(flow-used,val[i]));
			if(ii){
				totcost+=ii*cost[i];
				used+=ii;
				val[i]-=ii;
				val[i^1]+=ii;
				if(used==flow){
					break;
				}
			}
		}
	}
	return used;
}
inline void Dinic(){
	while(spfa()){
		dfs(S,INF);
	}
	printf("%d %d",maxflow,totcost);
}
int main(){
	scanf("%d%d%d%d",&N,&M,&S,&T);
	for(int i=1;i<=M;i++){
		int ii,jj,kk,ll;
		scanf("%d%d%d%d",&ii,&jj,&kk,&ll);
		add(ii,jj,kk,ll);
		add(jj,ii,0,-ll);
	}
	Dinic();
	return 0;
}
posted @ 2019-07-02 15:27  guoshaoyang  阅读(173)  评论(0编辑  收藏  举报