网络流-最大流-费用流-个人总结
i207M
2018-08-28 20:50:25
## 费用流速度受边数影响很大!
**看12月集训笔记**
介于我写的Dinic经常被卡,无奈,转写EK型Dinic。
个人认为,当图的深度比较浅时,这样会比较快;否则,还是多路增广较快
```cpp
int dfs(int x,int f)
{
if(x==T||!f) return f;
for(ri &i=cur[x]; i; i=nx[i])
if(d[v[i]]+1==d[x]&&w[i])
{
int t=dfs(v[i],min(f,w[i]));
if(t)
{
w[i]-=t,w[i^1]+=t;
return t;
}
}
return 0;
}
int dinic()
{
int ans=0;
while(bfs())
{
memcpy(cur,head,sizeof head);
int tmp=0;
while((tmp=dfs(S,inf))) ans+=tmp;
}
return ans;
}
```
![捕获.png](https://i.loli.net/2019/05/31/5cf0f65059e9054757.png)
## 网络流记得建反边
# 边的cnt记得设为1
## SPFA用STL的queue,动态大小
## 方格取数问题
如何满足相邻的点只选取一个呢?对于这种多选一的问题(~~仿佛二分图一样~~)我们采用的时最小割:
S->黑点,容量为点权
白点->T,容量为点权
每一个黑点->取该黑点会受到影响的白点,容量为inf
这样,如果ST联通,说明选择了相邻点,直到成为割才符合要求;
注意:
一定要在适当的位置改变n的大小,n在输入时为行数,在ISAP时为点数;
## 方格取数加强版
按照网格建立网络,每个点拆成入点和出点,入点向出点连$flow=1,cost=-val$的边(负数是为了把最大费用最大流改成最小费用最大流),再连$flow=inf,cost=0$的边,表示第二次经过;
然后每个点向右和下连inf的边,S向1,1连容量为K的边,n,n向T连边;
~~只能向右和下连边,不能四个方向,不然有负环,SPFA挂~~
## [SCOI2007]修车
**注意到,一辆车排到了一个工人的第k个,这个工人一共要修n辆车,那么修理它的代价就是$time\times (n-k)$**
将m个工人拆成n个点,表示n个时间段;
将n个顾客连向$N\times M$个点,边权为修理时间×时间段,流量为1;
源点向N个顾客连边,流1费0;$N\times M$个工人点向汇点连边,流1费0;
## 平面图最大流等于对偶图的最短路
如图。
![](https://cdn.luogu.com.cn/upload/pic/45680.png)
啥?你说如何区分源汇点?
连一条原图S->T的边在最外面,有框出来一个新的平面为S,外面的为T
![](https://cdn.luogu.com.cn/upload/pic/45682.png)
例题:狼抓兔子
但是不要被迷惑了,因为《狼抓兔子》的正反边权一样,但是遇到《海拔》这种题,正反边权不一样的情况,一定要画图,**好好确定边的方向**。
## DAG最小代权路径覆盖
《星际竞速》
拆AB点,S向A连流1费0,B向T连流1费-a(代表不用瞬移到这里了),对于边$(u,v)$,连$(u_A,v_b)$流1费w(边权)。答案为$\sum a-$最小费用流(费用>=0就停止)。
**两部分不能选公共的:将公共的视为边,求独立集**
**如何在网络流中强制某条边必须满流?可以跑“有上下界的”网络流;也可以把它的边权设为-inf**
-------------
**利用自动的流量分配解决一类关于分配某种物资的问题**。
![捕获.PNG](https://i.loli.net/2019/03/12/5c87b5ecbf1ca.png)
## 网络流的有关矩阵的问题,都可以考虑二分染色
### 网络流的流有速度(例如一个单位时间走一条边),或是在某个时刻加入流量限制。通常通过拆点来解决
[HNOI2007]紧急疏散:二分时间T,将每个点拆成T个点。
## 两个集合的矛盾关系可以使用最小割建模
Google Code Jam 2014 Round2C
最大流->最小割->对偶图最短路。
Ski Resort
N × M 的矩阵,每个点有高度。可以花 1 的代价将某点的高度提高 1。每个点可以到相邻的不比它高的点。求最小代价使 A 不能到 B。
这道题使用最小割的方法非常巧妙,想到了按高度拆点,但是没想到如何应用到最小割。
![SKRIES.JPG](https://i.loli.net/2019/03/13/5c88b175b5d0e.jpg)
7是这个点,红点是它周围的点。
这样连,将高度差更大的的连在最底层,这样高度就形成了“或”的关系,只能选一个。
```cpp
for(ri i=1;i<=n;++i)
for(ri j=1;j<=m;++j)
{
static int xx[]={0,-1,0,1},yy[]={1,0,-1,0};
static pii b[5];
int cnt=0;
for(ri dir=0;dir<4;++dir)
{
int tx=i+xx[dir],ty=j+yy[dir];
if(tx<1||ty<1||tx>n||ty>m) continue;
if(a[tx][ty]>=a[i][j]) b[++cnt]=mp(tx,ty);
}
if((i==Ax&&j==Ay)||(i==Bx&&j==By))
{
for(ri o=1;o<=cnt;++o) adde(id[b[o].fi][b[o].se],id[i][j],inf);
continue;
}
sort(b+1,b+1+cnt,[](const pii &x,const pii &y)
{
return a[x.fi][x.se]>a[y.fi][y.se];
});
int pre=id[i][j];
for(ri o=1;o<=cnt;++o)
{
adde(++tot,pre,a[b[o].fi][b[o].se]+1-a[i][j]);
adde(id[b[o].fi][b[o].se],tot,inf);
pre=tot;
}
}
```
## 最小割可以解决的模型
每个点选或不选(01两种状态)
1. 每个点选01有代价。
2. 某两个选的不同有代价。
3. 若干个选的相同有代价。
这样建图:
在最小割中,与S相连表示不选,与T相连表示选。
对于3.,给每个集合新建一个点p,S向p连集合的代价,p向元素连inf,这表示集合里的元素都选的代价;再新建p',向T连集合的代价,元素向p'连inf,表示都不选的代价。
对于2.,连接$(x,y)$表示x不选而y选的代价
对于1.,S向每个点连选的代价,每个点向T连不选的代价。
**网络流的一种套路:先假设所有都选,再扣除最少的**。
CEOI 2008 order
有 N 个工作,M 个机器,每种机器可以按次数租用或是买过来,每个工作需要使用一些机器,完成后可以获得收益。求最大获利。
如果没有租用,就是一个裸的最大权闭合子图,而且还是二分图。
租用相当于一个**违反代价**:物品 A 可以不依赖于物品 B,但需要付出一定代价。将机器于任务之间连的边容量设为租用代价即可。
### SurroundingGame
N × M 的矩阵,每个点有选择代价和控制收益。一个点被控制当且仅当它被选择或者它的邻居都被选择。
最小割思想建图的应用。
注意到我们有哪些选择?
1.放弃收益
2.选择这个点
3.选择这个点周围的点
注意到最大权闭合子图很难处理“或”的关系,所以我们把3.变为:不选这个点且选择这个点周围的点,然后这样建图:
![](https://cdn.luogu.com.cn/upload/pic/54023.png)
### Boardpainting
N × M 的棋盘,有些格子有棋子。每次可以拿走同行或同列中若干连续的棋子。求至少多少次操作可以拿走所有棋子。
默认每个点都独立消除。若上下相邻两个棋子都是竖着拿,得到 1 的收益,若左右相邻两个棋子都是横着拿,得到 1 的收益。**最大权闭合子图模型**。
## 费用流
经典问题
**费用不为一次函数的问题**
拆边,用差分后的一次函数建边。
JSOI2009球队收益:一个常用Trick:先假设所有球队都输,然后用网络流调成赢。
**带下界的最小费用可行流问题**
如何在网络流中表示“区间加”的操作?
方法一:类似于“志愿者招募”,我们可以把每个点穿成一串$(i,i+1)$,这样区间加就相当于$(R+1,L)$
方法二:S向$[1,n]$连边,$(i,i+1)$,$[2,n+1]$向T连边。这样做的好处是可以通过限流来限制区间的选择数量。
**线性规划问题**
类比HNOI切糕,
![](https://cdn.luogu.com.cn/upload/pic/54061.png)
### 好题:[CTSC2009]移民站选址
横纵坐标无关。类比切糕的思想,每个B的坐标一定是A的坐标中的一个,这样$f(x,v)$就能处理出来了。然后解决B之间的贡献,可以在每条链上竖着连,这样最小割中间就能通过差分得到正确的贡献。
### BZOJ 3716
题意是最大权闭合子图的裸题,但是如何建图?我想了数据结构建图很久,结果正解是:**用新的基底表示空间,模拟最大流**
变成每个守卫可以向xy坐标大的物品流流量,然后扫描线,每个守卫优先向y大于它的最小的物品连边。
# 网络流24题
### 最长不下降子序列问题
emmmm这道题在思考的时候,想到了一个限制长度的方法:强制每个流必须从1进,从s出,中间的dp值严格+1,但是随后pass掉了这个做法,以为这样限制,可能不能选出最多的链。原来这个是可以证明最优解都可以转化为这样的形式,手玩几组就可以发现了。
~~woc我TM调了半个小时结果发现我连边的时候没判断!!!我最近是怎么了不在状态~~
### 最长k可重区间集问题
唔,好题,学到新姿势了。我们把端点离散化,然后用流k费0的边穿起来整条链,和ST也连起来,这样,对于线段$(l,r)$,连边$l\to r$表示如果选择这条边,那么经过$(l,r)$的流量都会-1。
### 最长k可重线段集问题
这道题相比上一题的区别在于横坐标可能相同,这样就有负环了,我们当然可以直接判掉(贪心的加入),也可以**拆点,使负环拆散**
--------------------
### CF739E Gosha is hunting
网络流表示代价的方法:可以在一对点中连多条边,表示第1,2...次选择的代价
这道题还有数据结构和wqs二分的做法。