文章持续更新(咕咕咕 )
在网络流的题目中,建模一定是题目中的重点+难点,
下面总结一些网络流的建模套路
一,最基础的建模:两种方案如何决策
例题: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;
}
其实,网络流的题目还是要多练习,才能快速建立模型,解决问题
欢迎各位大佬来踩