[笔记]网络流--最大流问题

[笔记]网络流--最大流问题

原题链

算法用途

​ 解决网络流最大流问题,一般使用的是EK算法或是Dinic算法,由于Dinic的复杂度更优秀,所以我用的是Dinic,这里也只讲这个算法EK不会啊

问题描述

一.几个概念

​ 1.网络:一个网络 \(G = (V,E)\)是一张有向图,图中每条有向边\((x,y) ∈ E\)都有一个给定的权值\(c(x,y)\),称为变的容量.图中还有两个特殊的给定的点\(S,T\)称为源点和汇点,也就是起点和终点.\(f(x,y)\)函数是指一个满足条件的流量方案,满足:

\[f(x,y) ≤ c(x,y)\\ f(x,y) = -f(y,x)\\ \]

\(f\)函数也可以成为边的流量.

​ 2.最大流问题:对于一个给定的网络,合法的流函数\(f\)有很多,使得整个网络流量\(Σ_{(s,v)∈E}f(s,v)\)最大,(S是源点)的流函数称为网络的最大流.

算法描述

一.大致流程

​ 1.用BFS找出可行的增广路(也就是可以增加流量的从源点到汇点的路径),并预处理出各个节点的深度.

​ 2.再用dfs对增广路增加流量,知道不存在增广路.

​ 3.重复一上两个步骤知道不能增广为止.

二.具体实现

​ 首先对每条弧存一条反向弧,初始流量为0,当正向弧剩余流量减少时,反向弧剩余流量随之增加,这样就为每条弧提供了一个反悔的机会,可以让一个流沿反向弧退回而去寻找更优的路线.对于一个网络流图,用bfs将图分层,只保留每个点到下一个层次的弧,目的是减少寻找增广路的代价.对于每一次可行的增广操作,用dfs的方法寻找一条由源点到汇点的路径并获得这条路径的流量c.根据这条路径修改整个图,将所经之处正向边流量减少c,反向边流量增加c.如此反复直到bfs找不到可行的增广路线.

三.优化

当前弧优化

​ 对于一个节点\(x\),当它在\(dfs\)中走到了第\(i\)条弧时,前\(i-1\)条弧到汇点的流一定已经被流满而没有可行的路线了.那么当下一次再访问\(x\)节点的时候,前\(i-1\)条弧就可以被删掉而没有任何意义了.所以我们可以在每次枚举节点\(x\)所连的弧时,改变枚举的起点,这样就可以删除起点以前的所有弧以达到优化的效果.

整体流量优化

​ 对于每一个节点,记录可以向下流的总流量再返回,就可以减少递归的次数.

代码

#include <bits/stdc++.h>
using namespace std;
struct node{
	long long to = 0,next = 0,w = 0;
}edge[50010];
long long fir[50010],cur[50010];
long long n,m,s,t,tot = 1;
long long dep[50010];
long long maxx = 0;
const int inf = INT_MAX;
void add(long long x,long long y,long long z){
	tot++;
	edge[tot].to = y;
	edge[tot].w = z;
	edge[tot].next = fir[x];
	fir[x] = tot;
	tot++;                 //建反向边方便反悔
	edge[tot].to = x;
	edge[tot].w = 0;
	edge[tot].next = fir[y];
	fir[y] = tot;
	return;
}
bool bfs(){
	queue < int > q;
	while(!q.empty())q.pop();
	memset(dep,0,sizeof(dep));
	q.push(s);dep[s] = 1;
	while(!q.empty()){
		int x = q.front();
		q.pop();
		for(int i = fir[x];i;i = edge[i].next){
			if(edge[i].w > 0 && dep[edge[i].to] == 0){
				q.push(edge[i].to);
				dep[edge[i].to] = dep[x] + 1;
				if(edge[i].to == t)return true;    //找到增广路
			}
		}
	}
	return false;
}
long long dinic(int x,long long flow){
	if(x == t)return flow;
	long long now = 0;
	for(int i = cur[x];i && flow;i = edge[i].next){
		cur[x] = i;      //记录当前搜的点,当前弧优化
		if(edge[i].w != 0 && dep[edge[i].to] == dep[x] + 1){
			int k = dinic(edge[i].to,min(edge[i].w,flow));
			if(k == 0)dep[edge[i].to] = 0;
			now += k;
			edge[i].w -= k;
			edge[i ^ 1].w += k;
			flow -= k;
		}
	}
	return now;
}
int main(){
	scanf("%lld%lld%lld%lld",&n,&m,&s,&t);
	for(int i = 1;i <= m;i++){
		long long x,y,z;
		scanf("%lld%lld%lld",&x,&y,&z);
		add(x,y,z);
	}
	while(bfs()){
		memmove(cur,fir,sizeof(cur));
		maxx += dinic(s,inf);
	}
	printf("%lld\n",maxx);
	return 0;
}

posted @ 2020-08-27 10:58  czyczy  阅读(229)  评论(0编辑  收藏  举报