首页 > 其他分享 >网络流学习笔记

网络流学习笔记

时间:2024-07-21 17:28:50浏览次数:15  
标签:head 增广 int ll 网络 笔记 学习 vis dis

今天重温了网络流,感觉收获颇丰
网络流其实可以抽象成流水问题,有\(n\)个点\(m\)条边,每条边有最大容量,有一个出水的源点\(S\)和进水的汇点\(T\),问你最后汇点的水最多能有多少。

  • 增广路:从\(S\)到\(T\)的一条路径中流的值都大于零的一条路就叫增广路

讲解法之前先介绍一下建反边的操作的意义:因为每次我们都会去寻找增广路但是我们并不知道当前这条是不是最优方案,因此我们需要一个反悔操作来帮我们找到最终答案,因此反边就出现了,反边的意思就是把原本是\((u->v)\)的路径反建一条,使每次在流到这条路径的时候在反边上减去在正边中流的流量,这样就可以做到下次再寻找增广路的时候反悔了。

EK

EK算法的核心就是每次在图中寻找一条增广路并更新流量,直到找不到新的增广路为止。时间复杂度是\(O(nm^2)\)
代码:

点击查看代码
#include <iostream>
#include <queue>

using namespace std;
typedef long long ll;
const int N = 5e3 + 10;

int n, m, s, t, tot = 1;
ll ans;
int pre[N], head[N];
ll dis[N];
bool vis[N];
struct zx{int to, nxt; ll w;} e[N * 2];

void add(int u, int v, ll w) {
	e[++tot] = {v, head[u], w};
	head[u] = tot;
}

bool bfs() {
	for(int i = 1; i <= n; ++i) vis[i] = 0;
	queue < int > q;
	q.push(s), vis[s] = 1, dis[s] = 2147483647;
	while(!q.empty()) {
		int u = q.front(); q.pop();
		for(int i = head[u]; i; i = e[i].nxt) {
			int v = e[i].to; ll w = e[i].w;
			if(!w || vis[v]) continue;
			pre[v] = i, vis[v] = 1;
			dis[v] = min(dis[u], w);
			q.push(v);
			if(v == t) return 1;
		}
	}
	return 0;
}
void work() {
	int x = t;
	while(x != s) {
		int v = pre[x];
		e[v].w -= dis[t];
		e[v ^ 1].w += dis[t];
		x = e[v ^ 1].to;
	}
	ans += dis[t];
}

int main() {
	cin >> n >> m >> s >> t;
	for(int i = 1, u, v, w; i <= m; ++i) 
		cin >> u >> v >> w, add(u, v, w), add(v, u, 0);
	while(bfs()) work();
	cout << ans << endl;
	return 0;
}

Dinic

EK算法因为每次只寻找一条增广路所以时间复杂度比较高,如果在稠密图中就显得不优秀,所以每次寻找多个增广路的Dinic算法就出现了。
Dinic算法的思路是对于离源点距离一样的点建分层图,然后在回溯时更新边权。
但是这样还是略慢,怎么办?别急,我们有当前弧优化。
当前弧优化的意思就是一条路已经被增广过一次了,那么它就不会再被增广了,那其实在增广的时候完全不需要考虑它了,那我们就另开一个数组记录当前点需要从那个点向后遍历就行了。
时间复杂度是\(O(n^2m)\),在稠密图中明显快于EK
代码:

点击查看代码
#include <iostream>
#include <queue>

using namespace std;
typedef long long ll;
const int N = 5e3 + 10, inf = 2147483647;

int n, m, s, t, tot = 1;
ll ans;
int dfn[N], cur[N], head[N];
struct zx{int to, nxt, w;} e[N * 2];

void add(int u, int v, int w) {
	e[++tot] = {v, head[u], w};
	head[u] = tot;
} 

bool bfs() {
	for(int i = 1; i <= n; ++i) dfn[i] = inf;
	queue < int > q;
	q.push(s), dfn[s] = 0, cur[s] = head[s];
	while(!q.empty()) {
		int u = q.front(); q.pop();
		for(int i = head[u]; i; i = e[i].nxt) {
			int v = e[i].to, w = e[i].w;
			if(dfn[v] != inf || !w) continue;
			q.push(v), dfn[v] = dfn[u] + 1, cur[v] = head[v];
			if(v == t) return 1;			
		}
	}
	return 0;
}

int dfs(int u, int k) {
	if(u == t) return k;
	int res = 0;
	for(int i = cur[u]; i && k; i = e[i].nxt) {
		int v = e[i].to, w = e[i].w; 
		cur[u] = i;
		if(!w || (dfn[v] != dfn[u] + 1)) continue;
		int x = dfs(v, min(k, w));
		if(!x) dfn[v] = inf; 
		res += x, k -= x; 
		e[i].w -= x, e[i ^ 1].w += x;
	}
	return res; 
}

int main() {
	cin >> n >> m >> s >> t;
	for(int i = 1, u, v, w; i <= m; ++i) 
		cin >> u >> v >> w, add(u, v, w), add(v, u, 0);
	while(bfs()) ans += dfs(s, inf);
	cout << ans << endl;
	return 0;
}

以上是最大流的讲解。

费用流:最小费用最大流

在考虑要最大流的同时需要使花费最小,显然普通的最大流完成不了这个,但是想要完成也很简单,只需要用SPFA或者dijkstra维护一下找增广路的过程就行了。
代码:

点击查看代码
#include <iostream>
#include <cstring>
#include <queue>

using namespace std;
typedef long long ll;
const int N = 5e5;

int n, m, s, t, tot = 1;
ll maxg, minc;
int pre[N], dis[N], mf[N], head[N];
bool vis[N];
struct zx{int to, nxt, w, dis;} e[N * 2];

void add(int u, int v, int w, int dis) {
	e[++tot] = {v, head[u], w, dis};
	head[u] = tot;
}

bool spfa() {
	queue < int > q;
	memset(dis, 0x3f, sizeof(dis));
	memset(vis, 0, sizeof(vis));
	q.push(s), dis[s] = 0, vis[s] = 1, mf[s] = 1 << 30;
	while(!q.empty()) {
		int u = q.front(); q.pop(); vis[u] = 0;
		for(int i = head[u]; i; i = e[i].nxt) {
			if(!e[i].w) continue;
			int v = e[i].to;
			if(dis[v] > dis[u] + e[i].dis) {
				dis[v] = dis[u] + e[i].dis;
				mf[v] = min(mf[u], e[i].w);
				pre[v] = i;
				if(!vis[v]) vis[v] = 1, q.push(v);
			}
		}
	}
	if(dis[t] == 1061109567) return 0;
	return 1;
}

void work() {
	maxg += mf[t], minc += mf[t] * dis[t];
	int u = t;
	while(u != s) {
		int i = pre[u];
		e[i].w -= mf[t], e[i ^ 1].w += mf[t];
		u = e[i ^ 1].to;
	} 
}

int main() {
	cin >> n >> m >> s >> t;
	for(int i = 1, u, v, w, dis; i <= m; ++i) 
		cin >> u >> v >> w >> dis, add(u, v, w, dis), add(v, u, 0, -dis);
	while(spfa()) work();
	cout << maxg << ' ' << minc << '\n';
	return 0;
}

标签:head,增广,int,ll,网络,笔记,学习,vis,dis
From: https://www.cnblogs.com/roselu/p/18314719

相关文章

  • CSA笔记4-包/源管理命令以及本地光盘仓库搭建
    包/源管理命令1.rpm是最基础的rmp包的安装命令,需要提前下载相关安装包和依赖包2.yum/dnf是基于rpm包的自动安装命令,可以自动在仓库中匹配安装软件和依赖包注意:以上是安装命令,以下是安装源3.光盘源:是指安装系统时后的操作系统光盘,它里面有很多自带的常用软件安装包,定位于当......
  • 推荐大家学习JAVA结合Al
    AI辅助下的Java学习计划目标设定-**初级阶段**:掌握Java基础语法,理解面向对象编程思想。-**进阶阶段**:熟练运用集合、多线程、网络编程等高级特性。-**实战项目**:完成至少两个综合项目,利用AI辅助提升代码质量和开发效率。-**理论深化**:深入学习Java虚拟机(JVM)原理、设......
  • 设计模式之观察者模式(学习笔记)
    定义观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会收到通知并自动更新。这种模式用于实现对象之间的解耦,使得一个对象的变化可以通知并更新多个依赖对象,而无需直接引用它们。为什么使用观察者模式?解耦观......
  • 迁移学习Transfer learning 与 元学习Meta-learning,二者的联系和差异
    基本概念:迁移学习tansferlearning迁移学习(tansferlearning):运用已有领域学到的知识来辅助新环境中的学习任务。新兴领域往往缺少大量训练数据,直接从头训练成本太高,而相关领域的知识学习是相似的,因此我们可以运用已有的相关知识(sourcedomain)迁移到新的学习任务(targetdomain)上......
  • 圆方树学习笔记 & 最短路 题解
    前言圆方树学习笔记,从一道例题讲起。题目链接:Hydro&bzoj。题意简述仙人掌上求两点距离。题目分析为了把仙人掌的性质发挥出来,考虑将其变成一棵树。圆方树就是这样转换的工具。先讲讲圆方树的概念:原图上的点为圆点,每个点双对应一个方点,树边都是方点连向点双内的圆点。具......
  • 2024年暑期学习 (1)
    2024年“春秋杯”网络安全联赛夏季赛0x00CTFstdout程序保护如下Arch:amd64-64-littleRELRO:PartialRELROStack:NocanaryfoundNX:NXenabledPIE:NoPIE(0x3fe000)这题的难点在于setvbuf(stdout,0LL,0,0LL)操......
  • Java 网络编程
    文章目录一、概念二、网络编程三要素三、UDP通信3.1发送端3.2接收端3.3运行结果四、TCP通信4.1发送端4.2接收端4.3运行结果五、三次握手、四次挥手5.1三次挥手(建立连接)5.5四次挥手(数据完整)一、概念在Java中,网络编程指的是计算机之间通过网络来进行通......
  • 蓝桥杯单片机学习(Day14 实现操作外部开启中断)
    外部中断相关寄存器的配置方法和触发方式:        实验配置:    [email protected],J3跳线配置为IO方式,J5配置为BTN、J2配置为1-3和2-4。配置方法:        EX0、IT0负责外部中断0服务函数的开启其中断服务函数优先级为interrupt0,EX1、IT1负责......
  • 蓝桥杯单片机学习(Day13 矩阵键盘 )
    现象:            按键S7、S11、S15、S19数码管显示00-03      按键S6、S10、S14、S18数码管显示04-07      按键S5、S9、S13、S17数码管显示08-11      按键S4、S8、S12、S16数码管显示12-15矩阵键盘介绍:    注......
  • 概率与计算 ----学习笔记
    概率与计算(定理、推论集合)Chapter\(I\)概率论公理定义\(1.1\) 概率空间三要素  \(1.\)样本空间\(\Omega\),限制在概率空间上的随机过程所以可能结果的集合  \(2.\)表示可容许事件的族集\(\mathcal{F}\),其中\(\mathcal{F}\)中的每个集合都是样本空间\(\Omeg......