首页 > 编程语言 >【算法】网络流初步

【算法】网络流初步

时间:2023-08-06 22:22:59浏览次数:40  
标签:return int sum 网络 初步 vis 算法 edge low

参考资料

用最通俗的语言让你学会网络流

OI-Wiki 网络流

算法学习笔记(28): 网络流

一、概念

网络流指的是在一个每条边都有容量的有向图中找到一种方案,使得源点到汇点的流量最大。

网络流问题常见的有三类,分别是最大流,费用流和最小割。

最大流顾名思义,表示的是在有向图中找到一种方案满足每条从源点到汇点的路径上的每条边的流量都不大于这条边的容量。比如下图:

image

若源点是 \(0\),汇点是 \(4\),那么从源点到汇点能流过的最大流量便是 \(6\)。

费用流问题便是在一个不仅每条边有容量还有费用的有向图上,求出最大流的同时也求出最小花费。

最小割问题是在这张有向图上割掉任意条边,使得源点和汇点不相通,要求割掉的边数最小。实际上,最小割就等于最大流。

二、实现

首先介绍增广路的概念。增广路指的是一条从源点到汇点的路径,其中的每一条边的残量都为正数。而网络流算法的核心在于找增广路。但由于无法控制找到增广路的顺序,每次单纯找一条增广路的算法并不能适用于网络流问题。

我们依靠反悔贪心的思路,在原图内引入反向边。每次寻找增广路,同时将这条增广路上的每一条边的反边的容量都加上这条边的流量。之后如果选了一条路的反边,相当于没有选这条路,这样就达到了贪心的目的。

1. EK 算法

bfs 暴力寻找增广路,直到找不到为止,此时的答案就是这张图的最大流。时间复杂度为 \(O(nm^2)\),其中 \(n\) 为点数,\(m\) 为边数。

当然,解决费用流问题时,bfs 也可以换成各种最短路径算法。下面给出的就是费用流代码。

bool spfa(){
	for(int i=1;i<=n;i++){
		dis[i]=(int)1e15;
		vis[i]=false;
	}
	dis[s]=0;
	queue<int>q;
	q.push(s);
	while(q.empty()==false){
		int x=q.front();
		q.pop();
		vis[x]=false;
		for(int i=head[x];i;i=edge[i].nex){
			int v=edge[i].to,w=edge[i].w,va=edge[i].v;
			if(w>0&&dis[v]>dis[x]+va){
				dis[v]=dis[x]+va;
				if(vis[v]==false){
					vis[v]=true;
					q.push(v);
				}
			}
		}
	}
	if(dis[t]==(int)1e15) return false;
	else return true;	
}

int dfs(int x,int low){
	vis[x]=true;
	if(x==t){
		ansi+=low;
		return low;
	}
	int used=0;
	for(int i=head[x];i;i=edge[i].nex){
		int v=edge[i].to,w=edge[i].w,va=edge[i].v;
		if(w>0&&(vis[v]==false||v==t)&&dis[v]==dis[x]+va){
			int sum=dfs(v,min(low-used,edge[i].w));
			if(sum==0) continue;
			val+=va*sum;
			edge[i].w-=sum;
			edge[i^1].w+=sum;
			used+=sum;
			if(used==low) break;
		}
	}
	return used;
}

void EK(){
	while(spfa()){
		vis[t]=true;
		while(vis[t]==true){
			memset(vis,0,sizeof(vis));
			dfs(s,INT_MAX);
		}
	}
	return;
}

2. Dinic 算法

Dinic 算法将有向图先用 bfs 跑出分层图,再在这个分层图上用 dfs 同时跑出多条增广路。时间复杂度为 \(O(n^2m)\)。

但事实上 Dinic 有一个常用的优化。我们发现在一次 dfs 中,它不得不遍历一篇当前点能到的所有边,即使这些边在前几次 dfs 中已经将流量跑满,没有再跑的可能了。所以我们引出当前弧优化,记录这一次跑满到了哪一条边,下一次直接从第一条没跑满的边开始遍历即可。

bool bfs(){
	for(int i=1;i<=n;i++){
		deep[i]=(int)1e9;
		cur[i]=head[i];
		vis[i]=q[i]=0;
	}
	tai=deep[s]=0;
	q[++tai]=s;
	while(tai!=0){
		int x=q[tai--];
		vis[x]=false;
		for(int i=head[x];i;i=edge[i].nex){
			int v=edge[i].to,w=edge[i].w;
			if(deep[v]>deep[x]+1&&w>0){
				deep[v]=deep[x]+1;
				if(vis[v]==false){
					q[++tai]=v;
					vis[v]=true;
				}
			}
		}
	}
	if(deep[t]==(int)1e9) return false;
	return true;
}

int dfs(int x,int low){
	if(x==t){
		ansi+=low;
		return low;
	}
	int used=0;
	for(int i=cur[x];i;i=edge[i].nex){
		cur[x]=i;
		int v=edge[i].to,w=edge[i].w;
		if(w>0&&deep[v]==deep[x]+1){
			int sum=dfs(v,min(low-used,w));
			if(sum==0) continue;
			used+=sum;
			edge[i].w-=sum;
			edge[i^1].w+=sum;
			if(used==low) break;
		}
	}
	return used;
}

int dinic(){
	while(bfs()==true){
		dfs(s,(int)1e15);
	}
	return ansi;
}

三、应用

事实上,网络流问题的难点在于建模。而可以用网络流解决的问题一般而言数据范围都较小。对于建模,限制可以转化成网络流边上的容量。

网络流还可用于解决二分图最大匹配问题,我们将源点向左边的点都连一条容量为 \(1\) 的边,右边的点向汇点同样连一条容量为 \(1\) 的边,这样保证左右两边的点都只跑一次。

标签:return,int,sum,网络,初步,vis,算法,edge,low
From: https://www.cnblogs.com/Arcka/p/17610187.html

相关文章

  • 扩展欧几里得算法与乘法逆元
    Part1:前置知识欧几里得算法\[\foralla,b\in\mathbb{N},\gcd(a,b)=\gcd(b,a\bmodb)\]\(\mathrm{Bézout}\)定理对于任意整数\(a,b\),存在一对整数\(x,y\),满足\(ax+by=\gcd(a,b)\)证明:在欧几里得算法的最后一步,即\(b=0\)时,显然有一对整数\(x=1,y=0\),使得\(a......
  • Linux网络服务之SSH服务
    目录SSH服务1.ssh基础2.ssh原理3.实际操作SSH服务1.ssh基础SSH(SecureShell)协议是一种安全通道协议对通信数据进行了加密处理,用于远程管理作用:主要用来实现字符界面的远程登录、远程复制等功能。SSH协议对通信双方的数据传输进行了加密处理,其中包括用户登录时输入的......
  • Dijkstra最短路径算法及其优化
    Dijkstra最短路径算法及其优化图示过程可以参考图文详解Dijkstra最短路径算法(freecodecamp.org)直接给出Java版本的普通实现方式:/***最普通的实现形式,时间复杂度是O(n2),空间复杂度是O(n)*@paramweight*@paramstart*@return*/publicstaticint[]getDij......
  • 【学习笔记】类欧几里得算法
    概述主要是求以下三个式子:\[f(a,b,c,n)=\sum_{i=0}^n\left\lfloor\dfrac{ai+b}{c}\right\rfloor\]\[g(a,b,c,n)=\sum_{i=0}^ni\left\lfloor\dfrac{ai+b}{c}\right\rfloor\]\[h(a,b,c,n)=\sum_{i=0}^n\left\lfloor\dfrac{ai+b}{c}\right\rfloor^2\]求\(f......
  • ubuntu中VMware虚拟机没有网络可能的原因
    有可能是VMware的网络服务未启动(比如注销账户再进入桌面时不会同时打开VMware的网络服务),可以去顶栏右上角点一下看看是不是这么回事。一般来说重启电脑就可以了,不行的话你就得研究怎么手动启动这个服务了。......
  • 网络安全等级保护定级指南
    对象安全保护等级第一级:等级保护对象受到破坏后会对公民,法人和其他组织的合法权益造成一般损害,但不危害国家安全,社会秩序和公共权益第二级:等级保护对象受到破坏后会对公民,法人和其他组织的合法权益造成严重损害,或对社会秩序和公共利益造成危害,但不危害国家安全第三级:等级......
  • 算法 华为
     1、链表,两两交换位置,不允许修改值,只能改节点例如1234,=>21432、拔河比赛选拔队员,输入身高,体重。按这两个优先级排序例如输入1827019060输出1906019060 3、最小花费问题(这个分值200,比前面的难)输入产品数量n,需要输出k种方案n个产品对应的价格数组输出:前k小......
  • 4 Linux网络编程
    4Linux网络编程4.1网络结构模式C/S结构:服务器/客户机,即Client-Server(C/S)结构。B/S结构:浏览器/服务器,即Browser/Server(B/S)结构4.2MAC地址、IP地址和端口4.2.1MAC地址MAC地址(MediaAccessControlAddress),在OSI模型中,第三层网络层负责IP地址,第二层数据链路层......
  • 神经网络可视化工具
    目录1.pipinstallonnx2.根据以下脚本把.pt文件转换成.onnx文件3.在网页打开onnx文件进行可视化展示使用onnx工具能可视化的展示神经网络的结构,便于理解和学习。1.pipinstallonnx2.根据以下脚本把.pt文件转换成.onnx文件"""ExportsaYOLOv5*.ptmodeltoONNXandTorc......
  • 数据结构与算法(四):双向链表
    基本概念双向链表概念和单向链表是一致的,区别在于双向链表在单向链表的基础上,指针区域多了一个指向上一个节点的指针。单向链表内容可以参考我的上一篇文章:http://t.csdn.cn/Iu56H。基本的数据结构如图所示:链表结构双向链表结构包含了节点的数据内容和两个指针:指向前一个节点......