网络流基础

本文介绍了EK算法(基于FF算法的BFS版本)和Dinic算法,重点讨论了如何避免FF算法中的回头路问题,通过引入深度概念和深度优先搜索来优化流量分配。文章通过代码示例展示了这两种在流量网络中的关键算法应用。
摘要由CSDN通过智能技术生成

书接上回,我们继续

EK算法

EK算法即为FF算法的BFS版,所以直接上代码:

其实可以不看,因为没人用

#include <bits/stdc++.h>
#define N 2001
#define M 20002
using namespace std;
const int INF=0X3f3f3f3f;
int head[N],v[N],ver[M],edge[M],Next[M],n,m,tot;
int incf[N],pre[N],s,t,maxflow;
bool bfs(){
	memset(v,0x00,sizeof(v));
	queue<int> Q;
	Q.push(s);
	v[s] = 1;
	incf[s] = INF;
	while(Q.size()){
		int x = Q.front();
		Q.pop();
		for(int i = head[x];~i;i = Next[i]){
			if(edge[i]){
				int y = ver[i];
				if(v[y]){
					continue;
				}
				incf[y] = min(incf[x], edge[i]);
				pre[y] = i;
				Q.push(y);
				v[y] = 1;
				if(y == t){
					return 1;
				}
			}
		}
	} 
	return 0;
}
void ADD(int x,int y,int z){
	ver[++tot] = y, edge[tot] = z, Next[tot] = head[x], head[x] = tot;
	ver[++tot] = x, edge[tot] = 0, Next[tot] = head[y], head[y] = tot;
}
void update(){
	int x = t, i;
	while(x != s){
		i = pre[x];
		edge[i] -= incf[t];
		edge[i ^ 1] += incf[t];
		x = ver[i ^ 1];
	}
	maxflow += incf[t];
}
signed main(){
	int x, y, z;
	while(~scanf("%d %d %d %d",&n,&m,&s,&t)){
		memset(head, -1, sizeof(head));
		tot = -1;
        maxflow = 0;
		for(int i = 1;i <= m;i ++ ){
			scanf("%d %d %d", &x, &y, &z);
			ADD(x, y, z);
		}
		while(bfs()){
			update();
		}
		printf("%d\n",maxflow); 
	}
	return 0;
}

Dinic算法

那么就到了我们的重头戏,Dinic算法!

在之前我们提到的FF算法和EK算法中,我们发现一个问题——我们会走回头路,走着走着离汇点更远了!

为解决这个问题,Dinic算法引入一个新的维度——深度,我们只走深度比自己深的点

我们结合代码分析:

int head[N], tot = 0;
struct node{
	int to, w, nxt;
}edge[M];
void add(int u, int v, int w){
	edge[tot].w = w;
	edge[tot].to = v;
	edge[tot].nxt = head[u];
	head[u] = tot ++ ;
}

加边我是用 0 0 0 为起点

int cur[N], dep[N];
bool bfs(){
	memset(dep, -1, sizeof dep);
	queue<int> Q;
	Q.push(s);
	dep[s] = 0;
	cur[s] = head[s];
	while(Q.size()){
		int u = Q.front();
		Q.pop();
		for(int i = head[u];~i;i = edge[i].nxt){
			int v = edge[i].to;
			if(dep[v] == -1 && edge[i].w){//如果没有遍历到过
				dep[v] = dep[u] + 1;//比父节点深1
				cur[v] = head[v];
				if(v == t) return true;
				Q.push(v);
			}
		} 
	} 
	return false;
}

我通过 d e p dep dep 记录深度,并将其赋为 − 1 -1 1

其中 c u r cur cur 为当前弧优化

整个遍历通过 b f s bfs bfs 实现,并且还承担着判断残余网络是否联通(有一条从 S S S T T T 的路径)的作用

int find(int u, int limit){
	if(u == t){
		return limit;
	}
	int flow = 0;
	for(int i = cur[u]; ~i && flow < limit; i = edge[i].nxt){ 
		int v = edge[i].to;
		if(dep[v] == dep[u] + 1 && edge[i].w){
			int z = find(v, min(edge[i].w, limit - flow));//一条路上最大的流量应小于最小的容量(短板)
			if(!z) dep[v] = -1;//若没有增广路,则可以将这个点删掉
			edge[i].w -= z;//减去流量
			edge[i ^ 1].w += z;//加上流量
			flow += z; 
		}
	}
	return flow;
}

通过dfs便利,寻找增广路

int dinic(){
	int r = 0, flow;
	while(bfs()){
		while(flow = find(s, INF)){
			r += flow; 
		}
	}
	return r;
}

重复查找直到没有增广路

代码奉上:

#include <bits/stdc++.h>
#define N 10005
#define M 200005
#define INF 1e8
#define int long long
using namespace std;

int n, m, s, t, u, v, w;
int head[N], tot = 0;
struct node{
	int to, w, nxt;
}edge[M];
void add(int u, int v, int w){
	edge[tot].w = w;
	edge[tot].to = v;
	edge[tot].nxt = head[u];
	head[u] = tot ++ ;
}

int cur[N], dep[N];
bool bfs(){
	memset(dep, -1, sizeof dep);
	queue<int> Q;
	Q.push(s);
	dep[s] = 0;
	cur[s] = head[s];
	while(Q.size()){
		int u = Q.front();
		Q.pop();
		for(int i = head[u];~i;i = edge[i].nxt){
			int v = edge[i].to;
			if(dep[v] == -1 && edge[i].w){
				dep[v] = dep[u] + 1;
				cur[v] = head[v];
				if(v == t) return true;
				Q.push(v);
			}
		} 
	} 
	return false;
}
int find(int u, int limit){
	if(u == t){
		return limit;
	}
	int flow = 0;
	for(int i = cur[u]; ~i && flow < limit; i = edge[i].nxt){ 
		int v = edge[i].to;
		if(dep[v] == dep[u] + 1 && edge[i].w){
			int z = find(v, min(edge[i].w, limit - flow));
			if(!z) dep[v] = -1;
			edge[i].w -= z;
			edge[i ^ 1].w += z;
			flow += z; 
		}
	}
	return flow;
}
int dinic(){
	int r = 0, flow;
	while(bfs()){
		while(flow = find(s, INF)){
			r += flow; 
		}
	}
	return r;
}
signed main(){
	scanf("%lld %lld %lld %lld", &n, &m, &s, &t);
	memset(head, -1, sizeof head);
	for(long long i = 1;i <= m;i ++ ){
		scanf("%lld %lld %lld", &u, &v, &w);
		add(u, v, w);
		add(v, u, 0);
	}
	printf("%lld",dinic());
	return 0;
}

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值