首页 > 其他分享 >网络最大流

网络最大流

时间:2023-12-25 21:48:34浏览次数:29  
标签:return 最大 int ll flow 网络 push es

关于 vector 存图

很多网上的资料(视频、题解)的最大流算法为了方便找反边,都使用了链式前向星。

但是!

vector 党表示不服!

于是在进行学习后,笔者归纳出了两种 vector 存图并快速找反边的方法。

存储反边编号

一般 vector 实现邻接表是形如这样的:(在最大流相关算法中)

struct edge
{
	int v,w;
};
vector<edge> e[N];
inline void addedge(int u,int v,int w)
{
	e[u].push_back({v,w});
	e[v].push_back({u,0});
}

但是这种存储方法无法快速找到反边。
考虑在结构体 edge 中添加一个信息 x

struct edge
{
	int v,w,x;
};

表示反边为 e[v][x]。那么加边时也相应的需要修改:

inline void addedge(int u,int v,int w)
{
	e[u].push_back({v,w,int(e[v].size())});
	e[v].push_back({u,0,int(e[u].size()-1)});
}

这样就可以快速实现找反边操作了。(关于为什么是 int(e[v].size())int(e[u].size()-1) 请自行思考。)

注意,EK 算法中 int pre[N] 数组则需要改成 pair<int,int> pre[N],分别表示来自 first 号点和它的 second 号边。

这里放出 EK 板子和 Dinic 板子:

EK:

#include <iostream>
#include <vector>
#include <bitset>
#include <queue>
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
constexpr int N=214;
constexpr ll INF=0x3f3f3f3f3f3f3f3f;
struct edge
{
	int v;
	ll c;
	int x;
};

vector<edge> e[N];

int n,m,s,t;

namespace EK
{
	bitset<N> vis;
	pii pre[N];
	ll flow[N];
	bool bfs()
	{
		queue<int> q;
		vis.reset();
		vis[s]=true;
		q.push(s);
		flow[s]=INF;
		int u,l,v;
		ll c;
		while(!q.empty())
		{
			u=q.front();
			q.pop();
			l=e[u].size();
			for(int i=0;i<l;i++)
			{
				v=e[u][i].v;
				c=e[u][i].c;
				if(!vis[v]&&c>0)
				{
					vis[v]=true;
					flow[v]=min(flow[u],c);
					pre[v]={u,i};
					if(v==t)return true;
					q.push(v);
				}
			}
		}
		return false;
	}
	ll EK()
	{
		ll r=0ll;
		while(bfs())
		{
			r+=flow[t];
			for(int i=t;i!=s;i=pre[i].first)
			{
				e[pre[i].first][pre[i].second].c-=flow[t];
				e[i][e[pre[i].first][pre[i].second].x].c+=flow[t];
			}
		}
		return r;
	}
}//^ namespace EK

int main()
{
	scanf("%d %d %d %d",&n,&m,&s,&t);
	for(int i=1,u,v,w;i<=m;i++)
	{
		scanf("%d %d %d",&u,&v,&w);
		e[u].push_back({v,ll(w),int(e[v].size())});
		e[v].push_back({u,0ll,int(e[u].size()-1)});
	}
	printf("%lld",EK::EK());
	return 0;
}

Dinic:

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
constexpr int N=214;
constexpr ll INF=0x3f3f3f3f3f3f3f3f;
struct edge
{
	int v;
	ll c;
	int x;
};

vector<edge> e[N];

int n,m,s,t;

namespace Dinic
{
	int dis[N],fir[N];
	bool bfs()
	{
		fill(fir,fir+N,0);
		fill(dis,dis+N,0);
		dis[s]=1;
		queue<int> q;
		q.push(s);
		int u,l,v;
		while(!q.empty())
		{
			u=q.front();
			q.pop();
			l=e[u].size();
			for(int i=0;i<l;i++)
			{
				v=e[u][i].v;
				if(!dis[v]&&e[u][i].c>0ll)
				{
					dis[v]=dis[u]+1;
					q.push(v);
					if(v==t)return true;
				}
			}
		}
		return false;
	}
	ll dfs(int u=s,ll flow=INF)
	{
		if(u==t)return flow;
		int l=e[u].size(),v;
		ll res=0ll,now,c;
		for(int i=fir[u];i<l;i++)
		{
			fir[u]=i;
			v=e[u][i].v;
			c=e[u][i].c;
			if(dis[v]==dis[u]+1&&c>0ll)
			{
				now=dfs(v,min(flow,c));
				if(now==0ll)dis[v]=0;
				e[u][i].c-=now;
				e[v][e[u][i].x].c+=now;
				res+=now;
				flow-=now;
				if(flow==0ll)return res;
			}
		}
		return res;
	}
	ll Dinic()
	{
		ll r=0ll;
		while(bfs())r+=dfs();
		return r;
	}
}//^ namespace Dinic

int main()
{
	scanf("%d %d %d %d",&n,&m,&s,&t);
	for(int i=1,u,v,w;i<=m;i++)
	{
		scanf("%d %d %d",&u,&v,&w);
		e[u].push_back({v,ll(w),int(e[v].size())});
		e[v].push_back({u,0ll,int(e[u].size()-1)});
	}
	printf("%lld",Dinic::Dinic());
	return 0;
}

将边存入池子并保存编号

我们可以使用两个 vector 实现更方便的存边方式:

vector<edge> es;
vector<int> e[N];

其中 es 存了所有边e[u] 中存 u 的所有出边es 中的编号

于是,如果我们需要知道边 e[u][i] 的信息,只需要访问 es[e[u][i]]

e[u][i] 的反边即为 e[u][i]^1,与传统链式前向星的访问反边方式类似。

存边时:

e[u].push_back(es.size());
es.push_back({v,ll(w)});
e[v].push_back(es.size());
es.push_back({u,0ll});

依然给出板子:

EK:

#include <iostream>
#include <vector>
#include <bitset>
#include <queue>
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
constexpr int N=214;
constexpr ll INF=0x3f3f3f3f3f3f3f3f;
struct edge
{
	int v;
	ll c;
};
vector<edge> es;
vector<int> e[N];

int n,m,s,t;

namespace EK
{
	bitset<N> vis;
	int pre[N];
	ll flow[N];
	bool bfs()
	{
		queue<int> q;
		vis.reset();
		vis[s]=true;
		q.push(s);
		flow[s]=INF;
		int u,l,v;
		ll c;
		while(!q.empty())
		{
			u=q.front();
			q.pop();
			l=e[u].size();
			for(int i=0;i<l;i++)
			{
				v=es[e[u][i]].v;
				c=es[e[u][i]].c;
				if(!vis[v]&&c>0)
				{
					vis[v]=true;
					flow[v]=min(flow[u],c);
					pre[v]=e[u][i];
					if(v==t)return true;
					q.push(v);
				}
			}
		}
		return false;
	}
	ll EK()
	{
		ll r=0ll;
		while(bfs())
		{
			r+=flow[t];
			for(int i=t;i!=s;i=es[pre[i]^1].v)
			{
				es[pre[i]].c-=flow[t];
				es[pre[i]^1].c+=flow[t];
			}
		}
		return r;
	}
}//^ namespace EK

int main()
{
	scanf("%d %d %d %d",&n,&m,&s,&t);
	for(int i=1,u,v,w;i<=m;i++)
	{
		scanf("%d %d %d",&u,&v,&w);
		e[u].push_back(es.size());
		es.push_back({v,ll(w)});
		e[v].push_back(es.size());
		es.push_back({u,0ll});
	}
	printf("%lld",EK::EK());
	return 0;
}

Dinic:

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
constexpr int N=214;
constexpr ll INF=0x3f3f3f3f3f3f3f3f;
struct edge
{
	int v;
	ll c;
};

vector<edge> es;
vector<int> e[N];

int n,m,s,t;

namespace Dinic
{
	int dis[N],fir[N];
	bool bfs()
	{
		fill(fir,fir+N,0);
		fill(dis,dis+N,0);
		dis[s]=1;
		queue<int> q;
		q.push(s);
		int u,l,v;
		while(!q.empty())
		{
			u=q.front();
			q.pop();
			l=e[u].size();
			for(int i=0;i<l;i++)
			{
				v=es[e[u][i]].v;
				if(!dis[v]&&es[e[u][i]].c>0ll)
				{
					dis[v]=dis[u]+1;
					q.push(v);
					if(v==t)return true;
				}
			}
		}
		return false;
	}
	ll dfs(int u=s,ll flow=INF)
	{
		if(u==t)return flow;
		int l=e[u].size(),v;
		ll res=0ll,now,c;
		for(int i=fir[u];i<l;i++)
		{
			fir[u]=i;
			v=es[e[u][i]].v;
			c=es[e[u][i]].c;
			if(dis[v]==dis[u]+1&&c>0ll)
			{
				now=dfs(v,min(flow,c));
				if(now==0ll)dis[v]=0;
				es[e[u][i]].c-=now;
				es[e[u][i]^1].c+=now;
				res+=now;
				flow-=now;
				if(flow==0ll)return res;
			}
		}
		return res;
	}
	ll Dinic()
	{
		ll r=0ll;
		while(bfs())r+=dfs();
		return r;
	}
}//^ namespace Dinic

int main()
{
	scanf("%d %d %d %d",&n,&m,&s,&t);
	for(int i=1,u,v,w;i<=m;i++)
	{
		scanf("%d %d %d",&u,&v,&w);
		e[u].push_back(es.size());
		es.push_back({v,ll(w)});
		e[v].push_back(es.size());
		es.push_back({u,0ll});
	}
	printf("%lld",Dinic::Dinic());
	return 0;
}

正文

咕……

标签:return,最大,int,ll,flow,网络,push,es
From: https://www.cnblogs.com/chargedcreeper/p/max-flow.html

相关文章

  • 自然语言处理的文本生成:从随机生成到神经网络生成
    1.背景介绍自然语言处理(NLP)是人工智能领域的一个重要分支,其主要目标是让计算机理解、生成和处理人类语言。文本生成是NLP中的一个关键任务,旨在根据给定的输入生成连贯、合理的文本。在过去的几年里,随着深度学习和神经网络技术的发展,文本生成的方法也发生了巨大变化。本文将从随机生......
  • vlan虚拟网络
    Vlan:虚拟局域网:把网络之间进行隔离,实现网络的安全Vlan用于交换机上1.刚开始接上交换机,PC11和PC12可ping通    2.交换机加入,vlan虚拟网则可实现两台电脑隔离Port default vlan 10将接口默认的vlan改为vlan10......
  • 【计算机网络中的CSMA/CD协议详解】
    (文章目录)什么是CSMA/CD协议?CSMA/CD协议是一种多路访问协议,用于以太网(Ethernet)局域网中。它的主要目的是确保多个计算机可以共享同一物理介质(例如,同一网络电缆)进行数据通信,而不会发生碰撞,从而导致数据包损坏。CSMA/CD的工作原理载波监听(CarrierSense):计算机在发送数据之前......
  • 数据通信与网络 4th
    《数据通信与网络》                                                            ......
  • 鸿蒙 Ark ui 实战登录界面请求网络实现教程
    团队介绍作者:徐庆团队:坚果派公众号:“大前端之旅”润开鸿生态技术专家,华为HDE,博客专家,超级个体,特邀嘉宾,签约作者,OpenHarmony布道师,电子发烧友专家博客,51CTO博客专家,擅长HarmonyOS/OpenHarmony应用开发、熟悉服务卡片开发。欢迎合作。前言:各位同学有段时间没有见面因为一直很忙所......
  • 高级计算机网络课程结课论文——《5G AKA协议安全性分析综述》
    4 结论与展望4.1  对未来5G演进的安全挑战的展望5G网络的迅猛发展给人类带来了巨大的技术创新和便利性,提供了诸多方便,但同时也伴随着更为复杂和严峻的网络安全挑战,影响着人类的正常生活。在未来5G演进中,本论文可以预见到以下几个方面的安全挑战,它们分别是:(1)5G网络演进中可能面......
  • 2023"安洵杯"第六届网络安全挑战赛-Misc WP
    dacongのsecret题目我的解答:题目给出一张png图片和一个加密压缩包,压缩包里面还存在另一张jpg图片看名字就知道是盲水印。由于压缩包里的图片提不出来,因此是单图盲水印,我们使用工具得密码d@C0ng1scUt3!!!解压得到另一张图片,010分析一下得到一串字符一眼丁真压缩包逆过......
  • 水星 SG108 PRO/1.0 网络端口镜像 使用流程
    水星SG108PRO/1.0网络端口镜像使用流程购买链接https://item.jd.com/100001913315.html水星智能网管交换机客户端应用程序1.0.3https://service.mercurycom.com.cn/download-1830.html如上图1是端口1ip172.16.106.602是端口2ip172.16.106.1106是网络接入口......
  • 2023-2024-1学期20232412《网络空间安全导论》第六周学习总结
    教材学习总结初步认知应用安全在不同领域的应用了解身份认证与信任管理的方式认识隐私的定义及隐私保护方法了解云计算、物联网、人工智能的相关知识思维导图教材学习中的问题及解决方法问题1:对差分隐私的知识不够理解解决方式:向ChatGPT询问,寻求清晰的解释问题2:对比特......
  • VMware的网络环境
    虚拟机网络:1.检查虚拟机网络编辑器-以管理员方式运行VMware-打开虚拟网络编辑器-查看NAT方式下,虚拟子网的网段流程:打开VM虚拟机-----》选择`Edit`下的`VirtualNetworkEditor`-----》查看NAT方式下,虚拟子网的网段 ......