网络流模板 注释详细

17 篇文章 0 订阅

该写的都在注释里了,详阅

放俩B站视频

【算法讲堂】【电子科技大学】【ACM】网络流入门

网络流算法—Min Cost Flow 最小费用流问题详解

网络流重点在建模,在构图,算法只是模板,要刷题,就像dp

模板注释比较详细了,不懂的看视频,Dinic代码第四行还有一篇解析的链接

求解最大流 Dinic

#include <iostream>//dinic
#include <cstdio>//时间复杂度上界是M*N^2 但实际上Dinic算法比这个理论上界好得多。有的题甚至能跑一千万点 而N^3最多跑400点 
#include <queue>//如果所有边容量均为1,那么时间复杂度是O(min(N^0.67,M^0.5) * M) 对于二分图最大匹配这样的特殊图,时间复杂度是O(N^0.5 * M)
#include <cstring>//https://www.cnblogs.com/LUO77/p/6115057.html是解析 
#define mem(a,b) memset(a,b,sizeof(a))
const int M=1e6+10,N=1e3+10,INF=0x3f3f3f3f;
using namespace std;
int m;//n为节点数,m为边数
int s,e;//s是起点,e是终点
struct node { //链式前向星
	int to,next,w;
} edge[M];
int head[N],cnt;
int vis[N];//节点层数
void addedge(int from,int to,int w) {
	edge[cnt].to=to;
	edgecnt].next=head[from];
	edge[cnt].w=w;
	head[from]=cnt++;
} 
int bfs() {//建立层次网络,每次时间复杂度M,最多建立N个,所以总复杂度上限NM 
	//每次都要重新分层
	mem(vis,0);
	queue<int> que;
	vis[s]=1;//层数
	que.push(s);
	while(que.size()) {
		int u=que.front();
		que.pop();
		for(int i=head[u]; ~i; i=edge[i].next) {
			int to=edge[i].to;
			//如果当前流的水流值已经为0 或者 vis[to]层数划分不符合要求
			if(edge[i].w==0||vis[to]) continue;//不能写vis[to]!=vis[to]+1,vis[to]初始值全是0 应该写如果to没有被访问过(否则它的层数>0)
			que.push(to);
			vis[to]=vis[u]+1;
		}
	}
	return vis[e];//如果汇点不在层次网络中,即不存在增广路,则返回0,算法结束 
}
int dfs(int u,int flow) { 
	//到达终点或者当前流的值为0返回
	if(u==e||flow==0) return flow;
	for(int i=head[u]; ~i; i=edge[i].next) {
		head[u] = i;//当前弧优化 因为for循环的i更新的条件是w==0或w!=0但k==0,而k==0就意味着(45行)flow==0,总之这条路废了i才会更新,既然废了,那就把头结点换成新的i,以免再走一次废路浪费时间 
		int to=edge[i].to;
		int w=edge[i].w;
		//不符合增广路径的要求 或者 当前流的值为0
		if(vis[to]!=vis[u]+1||w==0) continue;
		//继续按照当前增广路径搜索。
		int k=dfs(to,min(flow,w));//下一波水流是管道容纳量和当前水流的最小值
		if(k>0) {
			//对边的只进行修改,同时保留回退的空间。
			edge[i].w-=k;
			edge[i^1].w+=k;//i^1是反边,用来反悔,这是我们建边的时候故意设置的
			return k;//0^1=1,2^1=3,也就是0反边是1,2反边是3,所以我们建立正向边是0,2,4,8,...,2k,反边是正边+1  0(+) 1(-) 2(+) 3(-)…… 
		}
	}
	return 0;
}
void dinic() {
	int ans=0,k=0;
	//bfs()分层找处增广路径
	while(bfs()) { //终点e层数为0说明汇点不在层次网络中,算法结束 要么一开始路就不通,要么路被流完了 
		//找出每条增广路径的最小流
		while((k=dfs(s,INF))>0) //初始水流设为无穷大
			//所有增广路径的最小流的和即是最大流
			ans+=k;
	}
	printf("%d",ans);
}
void init() {
	mem(head,-1);
	cnt=0;
}
int main() {
	init();
	scanf("%d%d%d",&m,&s,&e);
	for(int i=1;i<=m;i++){
		int a,b,v;
		scanf("%d%d%d",&a,&b,&v);
		addedge(a,b,v); 
		addedge(b,a,0);//反向边 
	} 
	dinic();
	return 0;
}
/*
例 
6 1 5
1 2 4
1 3 6
2 3 2
2 4 3
3 5 4
4 5 5
*/

求解最大流最小费用流 SPFA

#include<cstdio>
#include<cstring>
#include<queue>
#include<iostream>
const int N=5e3+50,INF=0x3f3f3f3f;
using namespace std;
bool vis[N];
int n, m, s, t;//节点数、边数、源点、汇点 
int u, v, c, w;//每条边的起点、终点、容量、费用 
int cost[N], pre[N], last[N], flow[N];//源点到该点的花费,该点的前驱,前驱的地址(cnt) , 限制该点增广路径的流
int maxFlow, minCost;//最大流,最小花费 
struct Edge {//链式前向星,不过next变成from,由从后向前遍历改为从前向后遍历 cnt要初始化为-1 
	int from, to, flow, cost;
} edge[N];
int head[N], cnt;
queue <int> q;
//最大流最小费用流
void addedge(int from, int to, int flow, int cost) { //建正边和反边
	edge[++cnt].from = head[from];//正边
	edge[cnt].to = to;
	edge[cnt].flow = flow;
	edge[cnt].cost = cost;
	head[from] = cnt;
	edge[++cnt].from = head[to];//反边
	edge[cnt].to = from;
	edge[cnt].flow = 0;
	edge[cnt].cost = -cost;
	head[to] = cnt;
}
bool SPFA(int s, int t) {
	memset(cost, 0x3f, sizeof(cost));
	memset(flow, 0x3f, sizeof(flow));
	memset(vis, 0, sizeof(vis));
	q.push(s);
	vis[s] = 1;
	cost[s] = 0;
	pre[t] = -1;
	while (!q.empty()) {
		int now = q.front();
		q.pop();
		vis[now] = 0;
		for (int i = head[now]; ~i; i = edge[i].from) {
			//如果流大于0说明这条边还有用的价值
			if (edge[i].flow>0 && cost[edge[i].to]>cost[now] + edge[i].cost) {
				cost[edge[i].to] = cost[now] + edge[i].cost;
				//记录最短路径,找出花费最小的增广路径
				//记录当前节点的上一个节点
				pre[edge[i].to] = now;
				//上一条边的地址
				last[edge[i].to] = i;
				//记录限制当前增广路径的流
				flow[edge[i].to] = min(flow[now], edge[i].flow);
				//最短路径算法
				if (!vis[edge[i].to]) {
					vis[edge[i].to] = 1;
					q.push(edge[i].to);
				}
			}
		}
	}
	return pre[t] != -1;
}
void MCMF() {//min cost max flow最大流最小花费 
	//SPFA(s,t)找出s->t的增广路径
	while (SPFA(s, t)) {
		int now = t;
		//记录最大流的大小
		maxFlow += flow[t];
		//记录当前增广路径的花费
		minCost += flow[t] * cost[t];
		while (now != s) {
			//对路径进行反向建边,方便回退
			//last[now]   now的上一条边的位置
			edge[last[now]].flow -= flow[t];
			edge[last[now] ^ 1].flow += flow[t];//反边 
			//now的父节点
			now = pre[now];
		}
	}
}
//spfa + dinic
int main() {
	ios::sync_with_stdio(false);
    cin.tie(0);
	memset(head, -1, sizeof(head));
	memset(pre,0,sizeof(pre));
	cnt = -1;//初始化
	cout << "节点数为:";cin >> n;
	cout << "边数为:";cin >> m;
	cout << "源点编号为:";cin >> s;
	cout << "汇点编号为:";cin >> t;
	cout << "输入 " << m << " 条边的信息:" << endl;
	while (m--) {
		cout << "起点:";cin >> u;
		cout << "终点:";cin >> v;
		cout << "容量:";cin >> c;
		cout << "费用:";cin >> w;
		cout << "-----------------" << endl;
		addedge(u, v, c, w);
	}
	MCMF();
	cout << "最大流为:" << maxFlow << endl;
	cout << "最小费用为:" << minCost << endl;
	cout << endl;
	system("pause");
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值