`
duoerbasilu
  • 浏览: 1541659 次
文章分类
社区版块
存档分类
最新评论

网络流总结(二)

 
阅读更多

这些天学习网络流,总结了一下用到的主要算法,主要从下面几个方面来介绍

一、常见的几种算法

二、这些算法的复杂度

三、这些算法适合处理的问题

四、算法模板

FF方法(Ford_Fulkerson):

所有增广路径问题都是以Ford_Fulkerson方法为基础,之所以称为方法而不是算法,因为它提供的是一种思想。

Ford_Fulkerson(s,t)
	f = 0,对自定义流f进行初始化
	while 存在增广路径p 
		do 沿p对流f进行增广 //f += MaxFlow,MaxFlow为增广路径中最大流
	return p; //p即为整个图中最大流
我原来这个方法一直没有得到很好的理解,汗,现在才明白为什么叫增广路径,f是自己定义的一个流,原来图中存在容量c(i,j),在不超过c(i,j)的情况下尽情对f流进行增光,这样得到的f即为最大流。残留容量cf(i, j) = c(i, j) - f(i, j),网中所有cf(i, j) > 0 的边组成一个残留网络,每次对f进行一次增广,则cf(i, j) -= flow; cf(j, i) += flow;对反向边进行操作也是后面才了解,是为了给后面查找增广路径时候提供更多的选择,如果这个点不通了,则返回到上一个节点继续查找。这样最后残留网络中反向边流量cf(j, i) = f(i, j)。增广路径即s 到 t的一条路径。
沿增广路径进行增广的操作一般都是相同的,主要算法不同体现在对增广路径的寻找过程中。

Sap算法(Shortest Augmenting Path):

这是一类算法,每次寻找最短最广路径进行增广,后面的DK,Dinic算法都是基于此

Sap()
	ans = 0
	while 寻找增广路径
		do 存在一条最短增广路径p
		flow = min(cf(i, j) [i,j] ∈ E)
		ans += flow
		沿p进行增广 //正反两步
	return ans;

EK算法(Edmonds_Karp):

BFS寻找最短最广路径,最简单的一种寻找最短最广路径方法,算法复杂度为O(V*E^2),因为BFS寻找最短路径需要搜索完所有小于最短距离的边才能找到终点,所以算法复杂度比较高。

算法模板:

int path[nMax];
int queue[nMax];
int flow[nMax];
int s, t;
ek_Bfs()//算法复杂度O(E)
{
	int front, rear;
	front = rear = 0;
	queue[rear ++] = s;
	flow[s] = INF;
	while(front < rear)
	{
		int u = queue[front ++];
		if(u == t) break;
		for(int v = 0; v < n; ++ v)//其实就是搜索临边的意思
		{
			if(path[v] == -1 && map[u][v])
			{
				flow[v] = min(flow[u], map[u][v]);
				path[v] = u;
				queue[rear ++] = v;
			}
		}
	}
	if(path[t] == -1) return 0;
	else return flow[t];
}
ek_Flow()//算法复杂度O(V*E)
{
	int MaxFlow = 0;
	while((flow = ek_Bfs())
	{
		MaxFlow += flow;
		int cur = t;
		while(cur != s)
		{
			int pre = path[cur];
			map[pre][cur] -= flow;
			map[cur][pre] += flow;
			cur = pre;
		}
	}
	return MaxFlow;
}

Dinic算法:

算法思路:BFS寻找终点太慢,DFS又无法保证搜索到最短路径,结合这两种算法的优势,利用构造分层网络的算法来寻找最短路径,这个算法又称为阻塞流算法。
首先从s点处进行bfs搜索,dis[s] = 0,然后层次数依次增加,如果搜索到t,则结束。dfs进行搜索,逐层搜索,进行d[v] == d[u] + 1判断,任意一条路径即为最短路径。
Dinic 算法的另一个优化之处:找一条增广路径同时可以找到多条,类似增广路径树。如果cur顶点的总残余流量不为零,这样我们就不必要从起点再次开始寻找增广路,而是从cur顶点出发直接开始,这样就会减少了重复计算,提高效率。

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

模板:

struct Edge
{
	int v, w, next;
}adj[mMax];//邻接表,注意这里的下标是mMax,一般为nMax的10倍
int head[nMax];//顶点对应第一条弧
int dis[nMax];//到s的距离
int queue[nMax];
int s, t;//源点、汇点

int dinicBfs()//BFS建层次图,只要发现存在可行弧即可结束
{
	int front, rear;
	front = rear = 0;
	queue[rear ++] = s;
	memset(dis, -1, sizeof(dis));
	dis[s] = 0;
	while(front < rear)
	{
		int u = queue[front ++];
		for(int id = head[u]; id != -1; id = adj[id].next)
		{
			int v = adj[id].v;
			if(adj[id].w && dis[v] == -1)
			{
				dis[v] = dis[u] + 1;
				if(v == t) return 1;
				queue[rear ++] = v;
			}
		}
	}
	return 0;
}

int dinicDfs(int cur, int cost = INF)//DFS寻找增广路经,其中可能涉及到多条增广路经
	//①、②处,曾经漏写
{
	if(cur == t) return cost;//①
	int flow;//从v开始搜索到增广路经的最大流
	int ans = 0;//多条增广路经最大流之和
	for(int id = head[cur]; id != -1; id = adj[id].next)
	{
		int v = adj[id].v;
		if(adj[id].w && dis[v] == dis[cur] + 1 && (flow = dinicDfs(v, min(cost, adj[id].w))))
		{
			adj[id].w -= flow;
			adj[id ^ 1].w += flow;
			cost -= flow;
			ans += flow;
			if(!cost) break;//②,如果剩余流量为0,则需要退出
		}
	}
	return ans;
}

int dinicFlow()
{
	int ans = 0;
	while(dinicBfs())//判断是否存在可行弧
		ans += dinicDfs(s);
	return ans;
}

ISAP算法(sap优化算法):

算法思路:Dinic算法效率已经很高,然而算法的优化时无尽头的,Dinic算法需要多次计算层次图,增加了复杂度,是不是可以不多次计算层次图呢?答案是肯定的,这就是ISAP算法。
ISAP计算的是反向图的层次图,作用与原图的层次图一样。计算反向图的层次图是便于重新给顶点标号,即计算层次图,具体做法:在查找<u,v>的时候,MinDis = min(dis[v]),这样查找玩所有与顶点u相关的边之后,dis[u] = dis[v] + 1,从而得到新的层次图。在刚开始寻找的时候,我们也不需要计算层次图,对所有dis[]都赋值为0即可,对效率没有多大影响。
另外ISAP算法的另一个优化在于:如果层次图出现断层,则直接结束。

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

模板:

struct Edge
{
	int v, w, next;
}adj[mMax];
int head[nMax];
int dis[nMax];
int vn[nMax];//每一层顶点的个数,便于判断层次图中是否出现断层
int s, t;
int NV;//图中总顶点数

int sapDfs(int cur, int cost)
{
	if(cur == t) return cost;
	int flow;
	int ans = 0;
	int MinDis = NV;
	for(int id = head[cur]; id != -1; id = adj[id].next)
	{
		int v = adj[id].v;
		if(adj[id].w)//下面两个if不能合并,因为需要对MinDis进行赋值
		{
			if(dis[v] + 1 == dis[cur])
			{
				flow = sapDfs(v, min(cost, adj[id].w));//这个不应该写到上面if语句中,因为需要下面的if语句进行断层的判断。
				adj[i].w -= flow;
				adj[i ^ 1].w += flow;
				ans += flow;
				cost -= flow;
				if(dis[s] >= NV) return ans;//判断从v进行dfs搜索的过程中是否出现断层
				if(!cost) break;//如果剩余流量为0,则需要跳出循环
			}
			if(dis[v] < MinDis)
				MinDis = dis[v];//MinDis存储与cur相连顶点的最小层次数
		}
	}
	if(!ans)
	{
		if(-- vn[dis[cur]] == 0) dis[s] = NV;//判断是否出现断层
		dis[cur] = MinDis + 1;//对顶点进行重标记
		++ vn[dis[cur]];
	}
	return ans;
}

int sapFlow()
{
	memset(dis, 0, sizeof(dis));
	memset(vn, 0, sizeof(vn));
	vn[0] = NV;
	int ans = 0;
	while(dis[s] < NV)//dis[s] < NV其实是你自己定义的一个终止条件,你也可以使用flag标记,因为如果真的出现dis[s] < NV,则其中必然会出现断层
		ans += sapDfs(s);
	return ans;
}

匈牙利算法:

匈牙利算法,主要处理二分图最大匹配问题,算法复杂度为O(V^3)
算法模板:
int link[nMax];
int useif[nMax];
int n, m;//A、B集合分别对应的顶点数

int getPath(int x)//寻找匹配边,算法复杂度O(m^2),即O(V^2)
{
	for(int i = 0; i < m; ++ i)
	{
		if(!useif[i] && map[x][i])
		{
			useif[i] = 1;
			if(link[i] == -1 && getPath(link[i]))//如果点i被匹配,检查点link[i]是否存在另外可匹配边
			{
				link[i] = x;
				return 1;
			}
		}
	}
	return 0;
}

int getNum()//返回最大匹配数,算法复杂度O(n),即O(V)
{
	int num = 0;
	memset(link, -1, sizeof(link));
	for(int i = 0; i < n; ++ i)
	{
		memset(useif, 0, sizeof(useif));
		num += getPath(i);
	}
	return num;
}

以上内容只是我的理解,这两篇博文总结的很不错,推荐一下!

http://www.cnblogs.com/longdouhzt/archive/2012/05/20/2510753.html

http://www.cnblogs.com/longdouhzt/archive/2012/05/20/2510743.html

图论继续学习:

http://starfall512.com/?p=272#comment-152


分享到:
评论

相关推荐

    ACM竞赛网络流算法讲义

    的学科在信息技术领域中扮演着至关重要的角色,网络流算法是图论的一个重要分支,它在计算机科学,尤其是算法竞赛如ACM(国际大学生程序设计竞赛)中有着广泛的应用。网络流问题通常涉及到在一个有向图中从源点到...

    网络流总结·小编

    网络流算法分析,谢谢~ 里面有各种实现的算法和代码,另外 SAP 为主要算法; 另外网络流的上下界、费用流的讲解都有哦。

    网络流算法和PASCAL程序

    ### 网络流算法及其应用 #### 定义 网络流理论是在图论中发展起来的一种理论与方法,主要用于解决网络上的一类最优化问题。1955年,T.E. 哈里斯首次提出了在一个给定的网络上寻求两点间最大运输量的问题。1956年,L...

    计算机网络协议总结

    计算机网络协议总结 计算机网络协议是计算机网络中实现数据传输和通信的基础。网络协议的种类繁多,各有其特点和应用场景。本文将对计算机网络协议进行总结,涵盖物理层、数据链路层、网络层、传输层和应用层等多个...

    网络流算法艺术

    总结来说,网络流算法的艺术在于如何巧妙地寻找和增加增广路径,以及如何在保证算法正确性的同时提高效率。Ford-Fulkerson方法提供了基础思想,而Edmonds-Karp和Dinic算法则是对此思想的具体实现和优化。理解这些...

    网络流教程(max_flow)

    ### 网络流教程(max_flow) #### 一、网络流概述 本教程旨在系统地介绍网络流(Network Flow)的基本概念、理论基础及其在计算机科学领域的应用。网络流问题是一类重要的图论问题,在很多实际场景中都有广泛的应用...

    《JAVA_IO流学习总结》

    八、网络流 - Socket和ServerSocket提供了网络通信的能力,相关的InputStream和OutputStream类用于在网络连接上进行数据交换。 九、NIO(New IO) Java NIO是Java 1.4引入的新特性,提供了非阻塞I/O操作,包括...

    Java海康威视网络摄像机和NVR录像机的SDK二次开发,实现对网络摄像机/NVR的实时流、历史流的推流功能以及抓图、录像下载等

    总结,Java进行海康威视网络摄像机和NVR录像机的SDK二次开发是一项涉及网络编程、视频处理、设备控制等多个领域的复杂任务。开发者需要深入理解SDK的使用,结合Java语言特性,构建稳定、高效的应用程序,以实现对...

    C#流使用总结

    总结,`Stream`在C#中扮演着至关重要的角色,无论是处理文件、内存、网络还是加密解密,都离不开它的身影。理解和熟练运用`Stream`及其子类,对于提升C#应用程序的I/O性能至关重要。通过实践和学习,开发者可以更好...

    动态网络流分类研究 动态网络流模型

    总结来说,动态网络流分类研究通过对网络流量的微观层面分析,揭示了用户的网络行为模式。这不仅增加了我们对网络交互行为的认识深度,而且为网络安全和网络管理提供了有力的技术支持。尽管网络行为的复杂性意味着这...

    经典网络流教程.ppt

    总结起来,网络流问题是一种优化问题,旨在最大化从源点到汇点的流量,同时遵守容量限制。在实际应用中,如本例所示的太空站迁移问题,网络流模型可以帮助我们找到最优的资源配置策略。通过使用如Ford-Fulkerson算法...

    计算机网络第二章总结完全.doc

    计算机网络第二章总结完全 计算机网络中,信道复用技术是一种非常重要的技术,它使得多个用户可以共享同一个信道进行通信。信道复用技术可以分为三种基本类型:频分复用(FDM)、时分复用(TDM)和统计时分复用...

    网络流问题

    ### 网络流问题详解 #### 一、网络流的基本概念 **网络流(Network Flows)**是图论中的一项重要研究领域,主要用于解决一类最优化问题。它不仅理论基础深厚,而且应用广泛,涉及物流优化、计算机网络设计等多个领域...

    网络流(最大流)SAP源码

    ### 网络流(最大流)SAP源码解析 #### 一、概述 本文主要解析一个关于网络流中的最短增广路径算法(Shortest Augmenting Path,简称SAP)的C++实现代码。该算法在解决最大流问题时通过选择最短路径进行增广来提高...

    网络流算法详解

    ### 网络流算法详解 #### 一、引言 网络流算法是计算机科学与数学领域中的一个重要分支,广泛应用于资源分配、物流规划、任务调度等实际问题中。其中,最大流问题是最基本的问题之一,它研究的是如何在网络中找到...

    基于道路的网络流模型

    ### 基于道路的网络流模型:优化疏散路径规划 #### 概述 本文介绍了一种基于道路的网络流模型,旨在优化区域疏散过程中的交通流,减少交通拥堵及延迟,特别是在交叉路口处发生的交通冲突。该模型是基于最小成本流...

    22考研-曲阜师范大学-网络知识点总结.docx

    "22考研-曲阜师范大学-网络知识点总结" 该资源主要总结了计算机网络的基础知识点,涵盖计算机网络的概念、组成与功能、网络的分类、物理层、数据链路层、网络层、传输层、应用层等重要知识点。 计算机网络的概念、...

    图割算法汇总及网络流介绍

    总结来说,图割算法主要处理图像分割和分类问题,而网络流理论是处理资源分配和传输问题的关键工具。通过福特-富克逊算法和圈算法,我们可以求解网络中的最大流和最小费用最大流,以实现优化目标。在实际应用中,...

    网络流算法的在信息学中的应用

    总结来说,网络流算法在网络流算法在网络流算法在网络流算法在信息学中的应用信息学中的应用信息学中的应用中的应用广泛且强大,能够处理多种实际问题。通过构造合适的网络模型并应用优化技巧,我们可以有效地求解...

    最大网络流Dinic算法

    总结起来,Dinic算法是解决最大网络流问题的一种高效方法,通过层次结构和增广路径的概念,能够在保证正确性的前提下,实现较好的运行效率。在实际应用中,它可以用于解决资源分配、调度、通信网络流量规划等问题。

Global site tag (gtag.js) - Google Analytics