ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2019-08-08 21:00:31  阅读:302  来源: 互联网

标签:待续 cnt EK 增广 int 算法 Edge include


目录

最大流流算法

(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


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

最小费用最大流

 

 

 

标签:待续,cnt,EK,增广,int,算法,Edge,include
来源: https://blog.csdn.net/castomere/article/details/98781496

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有