网络流算法//最大流//EK算法//dinic算法//最小(大)费用最大流(待续)

目录

最大流流算法

(EK算法)

时间复杂度 O(V*(E^2))

 

Dinic算法

时间复杂度O((V ^ 2) * E)


在洛谷题解中看到了一句很有启发的话:网络流善于解决各种有要求的匹配

联想到题目是匹配问题,且满足网络流要求的数据范围,可以尝试网络流

 

V是点的数目,E是边的数目

最大流流算法

(EK算法)

时间复杂度 O(V*(E^2))

定义

我们有一个 源点(只有流出量) 和 汇点(只有流入量)

每条边有一个 流量 和 容量,流量最初为0,容量是题目告知我们的,我们要求的是 在所有边的 流量 <= 容量 && 每个点流入量 = 流出量 时,流入汇点的最大流是多少。


首先我们需要知道残留网络

残留网络就是反向图的流量,我们正向遍历一个图,会走一条路径到汇点。

一条路径的   增广路 = min(经过的每条边的 {容量 - 当前流量} )

但是我们这条边可能是反向的流,所以

  • 正向边的容量 -= 增广路
  • 反向边容量 += 增广路

找到增广路,我们就 flow += 增广路

当找不到增广路就结束了这个算法。

因为看了vector建图和链式前向星建图的效率和空间差异,就手动改了一下模板,不用常用的mp[][] 数组标记,练习一下链式前向星。

洛谷3376

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <iostream>
#include <cmath>
#include <map>
#include <queue>
#include <algorithm>
#include <set>
#include <vector>
#include <stack>
#define Clear( x , y ) memset( x , y , sizeof(x) );
#define Qcin() std::ios::sync_with_stdio(false);
using namespace std;
typedef long long LL;
const int Maxn = 1e4 + 7;
const int MaxN = 2e5 + 7;
const int Inf = 1e9 + 7;
// O{V * E ^ 2}
/*
源点 st , 汇点 ed
*/
int N , M;
int st , ed;

int cnt;
struct edge{
	int Next;//下一条边的存储下标
	int to;//这条边的终点
	int w;//边权
};
edge Edge[MaxN];
int head[Maxn];//以 i 为起点的第一条边的标记
void AddEdge(int u , int v , int w){
	Edge[++cnt].Next = head[u];
	Edge[cnt].w = w;
	Edge[cnt].to = v;
	head[u] = cnt;
}

int pre[Maxn][2];
int Flow;
int flow[Maxn];
queue <int> que;
bool Bfs(){
	while(!que.empty())	que.pop();
	for(int i = 0 ; i <= N ; i++) pre[i][0] = pre[i][1] = -1;
	flow[st] = Inf;
	int q;
	que.push(st);
	while(!que.empty()){
		q = que.front(); que.pop();
		if(q == ed)	break;
		for(int i = head[q] ; ~i ; i = Edge[i].Next){
			int nq = Edge[i].to;
			if(nq != st && Edge[i].w && pre[nq][0] == -1){//下个点nq不是st,且边权>0,nq没被遍历过
				pre[nq][0] = i;//记录前一条边的编号
				pre[nq][1] = q;//记录nq前一个点q
				flow[nq] = min(Edge[i].w , flow[q]);//到nq路径上取min边权
				que.push(nq);
			}
		}
	}
	if(pre[ed][0] == -1)	return false;
	return true;
}

void MaxFlow(){
	int add , pl , fa , p;
	while(Bfs()){
		add = flow[ed];
		p = ed;
		while(p != st){
			pl = pre[p][0];//p上一条边的编号
			p = pre[p][1];//p变为它的上一个点
			Edge[pl].w -= add;
			Edge[pl^1].w += add;
		}
		Flow += add;
	}
	printf("%d\n",Flow);
	return;
}

void init(){
	for(int i = 0 ; i <= N ; i++)	head[i] = -1;
	cnt = -1;//从-1 开始 可以用 x ^ 1 来关联正反边
	return;
}

int main()
{
	scanf(" %d %d %d %d",&N,&M,&st,&ed);
	init();
	for(int i = 1 ; i <= M ; i++){
		int u , v , c , ct1 , ct2;	scanf(" %d %d %d",&u,&v,&c);
		AddEdge(u , v , c);
		AddEdge(v , u , 0);
	}
	MaxFlow();
	return 0;
}

 

Dinic算法

EK通常解决10^3 –10^4规模的网络 
而dinic能解决10^4–10^5的网络

时间复杂度O((V ^ 2) * E)

(真的快了不是一点,这一道题,Dinic是EK的 1/4 时间)

算法流程:

  • 在存在增广路的边上建立分层图,如果汇点不在存在增广路的分层图上,那么就不存在新的增广路,结束算法
  • 当存在汇点增广路,执行Dfs操作,在分层图中寻找增广路,增广路径从 u 到 v , 当且仅当dep[v] = dep[u] + 1
  • 找到增广路,Dfs回溯时减去增广路,一个节点可能减了多次增广路。

洛谷3376

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <iostream>
#include <cmath>
#include <map>
#include <queue>
#include <algorithm>
#include <set>
#include <vector>
#include <stack>
#define Clear( x , y ) memset( x , y , sizeof(x) );
#define Qcin() std::ios::sync_with_stdio(false);
using namespace std;
typedef long long LL;
const int Maxn = 1e4 + 7;
const int MaxN = 2e5 + 7;
const int Inf = 1e9 + 7;

int N , M;
int st , ed;
int Flow;

int cnt;
struct edge{
	int Next;
	int to;
	int w;
};
edge Edge[MaxN];
int head[Maxn];
void AddEdge(int u , int v , int w){
	Edge[++cnt].Next = head[u];
	Edge[cnt].w = w;
	Edge[cnt].to = v;
	head[u] = cnt;
}

int dep[Maxn];
queue <int> que;
bool Bfs(){
	for(int i = 1 ; i <= N ; ++i)	dep[i] = 0;
	while(!que.empty())	que.pop();
	dep[st] = 1;
	que.push(st);
	int q , v;
	while(!que.empty()){
		q = que.front(); que.pop();
		for(int i = head[q] ; ~i ; i = Edge[i].Next){
			v = Edge[i].to;
			if(Edge[i].w && !dep[v]){
				que.push(v);
				dep[v] = dep[q] + 1;
				if(v == ed)	return true;
			}
		}
	}
	return false;
}

int Dfs(int x , int flow){
	if(x == ed)	return flow;
	int res = flow , add;
	for(int i = head[x] ; ~i && res ; i = Edge[i].Next){
		int v = Edge[i].to;
		if(Edge[i].w && dep[v] == dep[x] + 1){
			add = Dfs(v , min(res , Edge[i].w));
			if(!add)	dep[v] = 0;
			Edge[i].w -= add;
			Edge[i^1].w += add;
			res -= add;
		}
	}
	return flow - res;
}

void Dinic(){
	int flow;
	while(Bfs()){
		while(flow = Dfs(st , Inf)){
			Flow += flow;
		}
	}
	printf("%d\n",Flow);
}
void init(){
	for(int i = 1 ; i <= N ; i++)	head[i] = -1;
	Flow = 0;
	cnt = -1;
}


int main()
{
	scanf(" %d %d %d %d",&N,&M,&st,&ed);
	init();
	for(int i = 1 ; i <= M ; i++){
		int u , v , c; scanf(" %d %d %d",&u,&v,&c);
		AddEdge(u , v , c);
		AddEdge(v , u , 0);
	}
	Dinic();
}

 

洛谷 P2763


我们已经学会了基础的最大流,那么就需要继续研究最小费用最大流 和 最大费用最大流

最小费用最大流

 

 

 

上一篇:POJ1149_PIGS(网络流/EK)


下一篇:ubuntu无法联网/暂时不能解析域名