文章持续更新(咕咕咕

在网络流的题目中,建模一定是题目中的重点+难点,

下面总结一些网络流的建模套路

一,最基础的建模:两种方案如何决策

例题:p1402 https://www.luogu.org/problemnew/show/P1402

为什么说是基础呢?因为题目中的条件很明显地指向两个方案,

对应流图中是连s还是t;

并且,要求最大满意数,就是利用网络流算法调节流量的功能,

做出一个最优的决策。

1,如何建立节点?如何连边?

网络流的建模中,流图中的每一条边都要对应题目中的一种状态,

所以,A喜欢菜品B,就将B与A连边,A喜欢房间C,就将A与C连边

注意,建立后的图一定是一个流图!(定义自行百度)

最后,s与所有菜品连边,房间与t连边;

2,容量的确定

确定容量非常重要,这题中;算法选择了一条边后,相当于满足了一个人的要求

所以,人与菜品或房间之间的边容量应为1

至于s与菜品连的边容量是多少呢?

我们不知道到底有多少人喜欢一种菜品;

后面已经有容量为1的边了,可以限制流量;

那么容量就是INF!

3,拆点的重要性

到这里,看似建模已经完成了,但是这里面有一个巨大的漏洞

试想一下,假设只有一个人,他喜欢100中菜品,喜欢100中房间

那么最大流跑出来会是多少呢?流量显然是100!

这就相当于只有1个人,算出来却满足了100人!

所以要进行拆点,将人拆成A1与A2,两者之间连一条容量为1的边,就可以了

拆点可以化点为边,进行流量限制

二,辅助点模拟决策集合

例题:p1361 https://www.luogu.org/problemnew/show/P1361

1,流量确定+最大流最小割定理

网络中,最大流=最小割

证明留给读者思考

在两个田地间有明确的选与不选的问题

在A种植获取利益a,那么s就与这种作物连一条容量为a的边;

在B种植获利b,就将作物与t连一条容量为b的边;

假设题目简化一下,没有说有一些作物在同一块田地中有额外利益;

那么就是简单的贪心,那边获利大往哪种,在图中体现为:

s到t每一条路的最大流==min(与s连的边,与t连的边);

算法中每割掉一条边,就等于选择另一条边

这样就很好理解了

2,虚点建立

这题还有一个要求,有几种作物同时种在A额外获利a;

同时种在B额外获利b

并且这几种作物还有他们各自的获利

在这种情况下,我们经常这么做:

建立辅助点(叫虚点也可以)X

s与X连一条容量为a的边,代表有这么一种情况,会获利a元

在将X与这些能够额外获利的作物连一条容量为INF的边

容量为INF,意味这它永远不会被割掉(被割掉的话就崩了)

善于运用虚点连边,有事可以很好地化繁为简

三,在网络中描述每一个决策

其实网络流所有的题目都一直在干这事;

这道题正确的建模可以很清楚地描述状态

有些题真的建模太不清楚(一定是我太菜了)

例题:p1251 https://www.luogu.org/problemnew/show/P1251

这题具体的方法可以看看某位dalao题解,写的很详细

https://www.luogu.org/blog/user31955/solution-p1251

四,网络流经典建模——互不攻击问题

题目的大致意思:在一个棋盘中,有些地方不能放棋子,

给定棋子的移动方式,求最多能放多少个棋子,使他们互不攻击。

例题:

p3355 https://www.luogu.org/problemnew/show/P3355

p5030 https://www.luogu.org/problemnew/show/P5030

下面直接给出步骤

1,找出处于那些位置时必定不能相互影响(具体见题解),

将其与s连边,val=1

2,将于s连边的那些点向它们能影响到的点连边,val=1

3,与s连边的点不能再与t连边,将那些会被影响到的点向t连边,val=1

(注意是会被影响到的点,如p5030中障碍点虽然不能放置棋子,

但是棋子可以跳到,所以应该与t连边;

而p3355中障碍点既不能放棋子,也不能跳到这个位置,所以不能与t连边)

4,二分图最大独立集 (与s连的点个数-最大流)即为最大摆放数

五,稍微毒瘤的建模——“一面对多面”问题

这个名字是题解中看到的,我觉得很形象

例题:p3980 https://www.luogu.org/problemnew/show/P3980

何为“一面对多面”呢?

那就是题目中的决策是有后效性的

这样就不能按照之前的方法;

所以建模是按照时间轴来

具体解法不多说,看题解都能懂

https://www.luogu.org/blog/dedicatus545/solution-p3980

六,“同时做”问题

这类问题的解法,我觉得挺独特的

例题:p2053 https://www.luogu.org/problemnew/show/P2053

总结起来就是:有多个人同时做不同的事;

一个人没有办法表示所有状态怎么办?

那就将每个人拿去拆

但这里的拆点与第一道例题的拆点作用天差地别

例题一中拆点是为了限制流量

而这一题中拆点是为了描述不同时间点的状态

将每个人,拆成多个点,表示不同时间点的人,再进行连边

同一位dalao的题解:

https://www.luogu.org/blog/dedicatus545/solution-p2053

———————————————毒瘤分割线————————————————

七,基于时间和空间的动态建模

这种题,emm怎么说呢,如果掌握了应该还好吧

如果初学能做出来是神仙

例题:p2754 https://www.luogu.org/problemnew/show/P2754

有没有感觉有点像“同时做”模型?但是不太一样

首先,需要建立两个坐标轴,纵坐标是时间,横坐标是空间

随着时间的流逝,每个飞船的空间会发生变化

纵坐标的单位长度是每个时间点;

横坐标的单位长度是每个飞船

这题特殊性就在于并不是求一次网络流就能得到答案

而是不断增加ans,每增加一次,动态连边,动态建立节点

直到到达t的流量等于人数,ans就是最优解;

但是看似这么复杂,还是在做那件事:

将题目中的所有状态在图中表示出来!

网络流的建模方法总结

我们对着题解中的图片看看:

1,每个星球上可以停留无数个人

-> 所有星球的时间点向各自的下一个时间点连一条容量为INF的边

2,飞船携带a人从A星球前往B星球,时间为一个单位

-> A星球的当前时间点向B星球的下一个时间点连一条容量为a的边

3,到达目标星球的人数

-> 到达t的流量

4,有k个人从起点星球出发

-> 有k的容量从s发出(其实INF也行,反正做飞船时已经限制了流量)

这样一分析,再结合图,感觉也没有那么毒瘤了吧?

———————————————良心分割线————————————————

下面奉上dinic的超级压行版~ (40行)来看看哪里还能更短吧

#include <bits/stdc++.h>
using namespace std;
struct Node {int ver,val,next;} node[M*2];
int head[100010],dep[100010],n,m,s,t,tot=1,x,y,val;
inline void add_edge(int x,int y,int val) {node[++tot].ver=y;node[tot].val=val;node[tot].next=head[x];head[x]=tot;}
inline void add(int x,int y,int val) {add_edge(x,y,val);add_edge(y,x,0);}
inline bool bfs() {
	queue<int> q;memset(dep,0,sizeof(dep)),dep[s]=0,q.push(s);
	while(!q.empty()) {
		int x=q.front();q.pop();
		for(register int i=head[x]; i; i=node[i].next) {
			int y=node[i].ver,val=node[i].val;
			if(!dep[y]&&val) {
				dep[y]=dep[x]+1,q.push(y);
				if(y==t)	return true;
			}
		}
	}
	return false;
}
int dinic(int x,int flow) {
	if(x==t)	return flow;
	int rest=flow,k; 
	for(register int i=head[x]; i&&rest; i=node[i].next) {
		int y=node[i].ver,val=node[i].val;
		if(dep[y]==dep[x]+1&&val) {
			k=dinic(y,min(val,rest));
			if(!k)	dep[y]=0;
			node[i].val-=k,node[i^1].val+=k,rest-=k;
		}
	}
	return flow-rest;
}
int main() {
	cin>>n>>m>>s>>t;
	for(register int i=1; i<=m; i++)	cin>>x>>y>>val,add(x,y,val);
	int maxflow=0,flow=0;
	while(bfs())	while(flow=dinic(s,1<<30))	maxflow+=flow;
	cout<<maxflow<<endl;
}

其实,网络流的题目还是要多练习,才能快速建立模型,解决问题

欢迎各位大佬来踩

相关文章: