网络流最大流——ISAP

在EK算法后,今天又学会了ISAP算法(比EK快)。
它比EK快的原因是EK算法一次找一条增广路,ISAP算法一次找多条增广路。
ISAP算法思路:
1.从t开始将t的深度标记为1,bfs一遍整张图(每条原边都有一条反向对应边),求出每一个点的深度(一个点的深度是t到这个点的最小距离+1,一条边的两个端点的距离为1)。
2.从s开始dfs,dfs中有两个元素——正在搜的点和从上一个点传过来的流量(flow),返回的是能传出或接受的流量,分三种情况讨论:
(1)正在搜的点是t时,flow全部能接受,在最大流值中加入flow,并返回flow。
(2)否则开始搜索它能到的点(它能到的点的深度是它的深度-1),传出去的流量是这条边的剩余流量限度和flow减去已经传出的流量(used)取最小值,used是每个返回值的和,这条边减去返回值,它的对应边加上返回值,再找完后或途中 \(used==flow\) (即从上一个点传入的流量全部被传出),深度不增加,直接返回used或flow,深度不增加的原因是它的上一层(深度-1的一层)中的它能到的点并未全部用完,可能还可以找出增广路。
(3)找完后 \(used<flow\) (那么它的上一层中它能到的点已全部用完或没用完的点也已经深度增加),这时它的深度加1,它本来所在深度的点的个数减1,现在所在的深度的点的个数加1,如果他原本所在的深度的点的个数变为了0,说明出现了断层,s不可能到t了,待回溯回s后结束dfs,输出最大流,结束程序。
下面是代码:

#include<iostream>
#define INF 0x3f3f3f
#define int long long
using namespace std;
int n,m,s,t;
struct node{
	int to;
	int val;
	int nxt;
}edge[10010];
int head[210],tot;
void addedge(int u,int v,int w){
	edge[++tot].to=v;
	edge[tot].nxt=head[u];
	edge[tot].val=w;
	head[u]=tot;
}
int deep[210];
int q[2010],he,ta;
int shu[210];
void bfs(){
	q[1]=t;
	he=1;ta=1;
	deep[t]=1;
	shu[1]=1;
	while(he<=ta){
		int u=q[he];
		he++;
		for(int i=head[u];i;i=edge[i].nxt){
			int v=edge[i].to;
			if(deep[v]==0){
				deep[v]=deep[u]+1;
				shu[deep[v]]++;
				q[++ta]=v;
			}
		}
	}
}
int maxflow;
int dfs(int u,int flow){
	if(u==t){
		maxflow+=flow;
		return flow;
	}
	int used=0;
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].to;
		if(edge[i].val!=0&&deep[u]==deep[v]+1){
			int zhi=dfs(v,min(edge[i].val,flow-used));
			if(zhi!=0){
				used+=zhi;
				edge[i].val-=zhi;
				if(i%2==1){
					edge[i+1].val+=zhi;
				}
				else{
					edge[i-1].val+=zhi;
				}
			}
			if(flow==used){
				return used;
			}
		}
	}
	shu[deep[u]]--;
	if(shu[deep[u]]==0)deep[s]=n+1;
	deep[u]++;
	shu[deep[u]]++;
	return used;
}
signed main(){
	cin>>n>>m>>s>>t;
	for(int i=1;i<=m;i++){
		int u,v,w;
		cin>>u>>v>>w;
		addedge(u,v,w);
		addedge(v,u,0);
	}
	bfs();
	maxflow=0;
	while(deep[s]<=n){
		dfs(s,INF);
	}
	cout<<maxflow;
	return 0;
}

到这里还可以加一个当前弧优化:
用一个cur数组代替head数组,在dfs中在查一条边时将cur[u]的更新成这条边的编号,原因:
前面的满足条件的边已经被用完了,也就是说这些边在这次dfs中已经不需要查了,这样减少了查的次数进行了优化。
但是在每一次新的dfs前都需要重新将cur赋成head的值,原因:

查边的顺序是5,4,3,2且3,5都被查完,cur[1]最后就会等于1,deep[1]++后但不将cur重新赋值为head的话,4就会被忽略掉,甚至3,5的deep++后也会被忽略,就会出错。
这时加了当前弧优化的代码:

#include<iostream>
#define INF 0x3f3f3f
#define int long long
using namespace std;
int n,m,s,t;
struct node{
	int to;
	int val;
	int nxt;
}edge[10010];
int head[210],tot;
void addedge(int u,int v,int w){
	edge[++tot].to=v;
	edge[tot].nxt=head[u];
	edge[tot].val=w;
	head[u]=tot;
}
int deep[210];
int q[2010],he,ta;
int shu[210];
void bfs(){
	q[1]=t;
	he=1;ta=1;
	deep[t]=1;
	shu[1]=1;
	while(he<=ta){
		int u=q[he];
		he++;
		for(int i=head[u];i;i=edge[i].nxt){
			int v=edge[i].to;
			if(deep[v]==0){
				deep[v]=deep[u]+1;
				shu[deep[v]]++;
				q[++ta]=v;
			}
		}
	}
}
int maxflow;
int cur[210];
int dfs(int u,int flow){
	if(u==t){
		maxflow+=flow;
		return flow;
	}
	int used=0;
	for(int i=cur[u];i;i=edge[i].nxt){
		cur[u]=i;
		int v=edge[i].to;
		if(edge[i].val!=0&&deep[u]==deep[v]+1){
			int zhi=dfs(v,min(edge[i].val,flow-used));
			if(zhi!=0){
				used+=zhi;
				edge[i].val-=zhi;
				if(i%2==1){
					edge[i+1].val+=zhi;
				}
				else{
					edge[i-1].val+=zhi;
				}
			}
			if(flow==used){
				return used;
			}
		}
	}
	shu[deep[u]]--;
	if(shu[deep[u]]==0)deep[s]=n+1;
	deep[u]++;
	shu[deep[u]]++;
	return used;
}
signed main(){
	cin>>n>>m>>s>>t;
	for(int i=1;i<=m;i++){
		int u,v,w;
		cin>>u>>v>>w;
		addedge(u,v,w);
		addedge(v,u,0);
	}
	bfs();
	maxflow=0;
	while(deep[s]<=n){
		for(int i=1;i<=n;i++){
			cur[i]=head[i];
		}
		dfs(s,INF);
	}
	cout<<maxflow;
	return 0;
}
posted @ 2022-03-29 19:45  zzzzzz2  阅读(130)  评论(3编辑  收藏  举报