初见 | 图论 | 网络流 | ISAP
前言
本篇是之前网络流入门的一个补充,涉及到一定的 \(\tt{Dinic}\) 芝士,如果不知道 \(\tt{Dinic}\) 建议先去再回来看那个。
本篇从我的一篇题解小改(其实基本上啥都没改)而来。
在这里顺便说一句,感觉我的 ISAP 实现有点问题,若有路过大佬,可以帮忙看一眼有什么神毕问题(
ISAP
何为 \(\tt{ISAP}\)?
\(\tt{ISAP}\) 算法和 \(\tt{EK、FF、Dinic}\) 这些算法一样,都属于增广路算法。也就是说其实她们本身的思路基本是一致的,只不过实现不同。
\(\tt{ISAP}\) 的全名为 \(\tt{Improved Shortest Augumenting Path}\),这个 \(\tt{Improved}\) 就体现在她相比于 Dinic,只需要一次 BFS。
这里就不再展开关于 Dinic 的过程描述,但由于下文涉及到 Dinic,这里仍建议大家在学习 ISAP 之前先保证已经学过了 Dinic 的流程,并且已经自己写过了一遍代码,下面的叙述中我就假定大家都会 Dinic 罢。
ISAP 的过程
-
从汇点 t 到源点 s 跑一次 BFS 进行深度标记
-
从源点 s 开始跑 DFS
第一步,和 Dinic 的从 s 到 t 跑正好相反。这样做是为了不再去反复的通过 BFS 对图进行分层。
于是我们只需要把 Dinic 的代码进行一定的修改就能完成 ISAP 的第一步:
struct node
{
LL to,nex,val;
}
r[MXX<<1];
LL head[NXX],cnt(1),dis[NXX],now[NXX],n,m,maxflow,s,t,gap[NXX];
queue<LL> qwq;
I void BFS()
{
qwq.push(t);dis[t]=0;now[t]=head[t];gap[0]=1;
while(qwq.size())
{
LL x=qwq.front();qwq.pop();
for(R LL i=head[x];i;i=r[i].nex)
{
if(!dis[r[i].to] and r[i].to!=t)
{
LL y=r[i].to;
qwq.push(y);
now[y]=head[y];
dis[y]=dis[x]+1;
++gap[dis[y]];
}
}
}
}
这里有了一个新的数组 gap[]
,gap[i]
代表的意义是深度为 i 的结点的数量,这是一个主要的优化。
因为第二步依然是 DFS 去走增广路,于是前面的部分和 Dinic 是一样的,只是最后稍有不同:
LL ISAP(LL x,LL flow)
{
if(x==t) Heriko flow;
LL rst=flow,k,i,y,mindis=inf;
for(i=now[x];i and rst;i=r[i].nex)
{
y=r[i].to;
if(r[i].val>0)
{
mindis=Hmin(mindis,dis[y]);
if(dis[y]+1==dis[x])
{
k=ISAP(y,Hmin(rst,r[i].val));
r[i].val-=k;
r[i^1].val+=k;
rst-=k;
}
}
}
if(!rst) Heriko flow;
//可以看到从这里往上的部分和 Dinic 的 DFS 基本一致
--gap[dis[x]];
if(!gap[dis[x]]) dis[s]=n+1;
if(mindis==inf) dis[x]=n;
else dis[x]=mindis+1;
++gap[dis[x]];
Heriko flow-rst;
}
前半部分的 DFS 和 Dinic 基本是一致的,只不过对层级 dis
的判断从 dis[x]+1==dis[y]
改为了 dis[y]+1==dis[x]
,这只是因为我们 BFS 分层的顺序不同了而已。
真正变化的就是 for 循环后面的一段(即上面代码中注释以下的部分)。
首先,既然程序能够运行到这里,说明本次 DFS 的起点 x 相连的点都已经走过了一遍并且流量还有剩余。那么此时我们对 x 的深度进行修改,然后更新其 gap
。
因为 ISAP 的 DFS 是按照深度走的,所以路径上的点的深度一定是连续的才能到达深度为 0 的 t,如果中间有任意一层的点不存在,我们都无法到达 t。因此当修改后的 gap
出现 0 的时候(说明这个深度已经没有点了),我们直接结束本次 DFS,这样能够有效的增加我们算法的效率。
这里给出一些图帮助理解:
ISAP 的那一次 BFS 会将此图分层(红色):
显然我们只能走深度连续的下路:
然后我们更新结点的深度和边的容量,得到新图:
然后走上路:
然后我们更新深度信息和边的容量,发现此时这张图出现了断层,停止 DFS。
ISAP 走这个过程非常简单,但是如果是 Dinic 就需要三遍 BFS,两遍 DFS!
也就是说 ISAP 利用深度更新少了整整两次分层过程!
Code
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <string.h>
#include <queue>
#define Heriko return
#define Deltana 0
#define Romanno 1
#define S signed
#define LL long long
#define R register
#define I inline
#define CI const LL
#define mst(a, b) memset(a, b, sizeof(a))
#define ON std::ios::sync_with_stdio(false)
using namespace std;
template<typename J>
I void fr(J &x)
{
short f=1;
char c=getchar();
x=0;
while(c<'0' or c>'9')
{
if(c=='-') f=-1;
c=getchar();
}
while (c>='0' and c<='9')
{
x=(x<<3)+(x<<1)+c-'0';
c=getchar();
}
x*=f;
}
template<typename J>
I void fw(J x,bool k)
{
if(x<0) putchar('-'),x=-x;
static short stak[35];
short top=0;
do
{
stak[top++]=x%10;
x/=10;
}
while(x);
while(top) putchar(stak[--top]+'0');
if(k) putchar('\n');
else putchar(' ');
}
template<typename J>
I J Hmin(J x,J y) {Heriko x<y?x:y;}
template<typename J>
I void Clear(queue<J> &x) {while(x.size()) x.pop();}
CI inf=998244353,MXX=5005,NXX=205;
struct node
{
LL to,nex,val;
}
r[MXX<<1];
LL head[NXX],cnt(1),dis[NXX],now[NXX],n,m,maxflow,s,t,gap[NXX];
queue<LL> qwq;
I void Ass_we_can(LL x,LL y,LL z)
{
r[++cnt].to=y;r[cnt].nex=head[x];r[cnt].val=z;head[x]=cnt;
r[++cnt].to=x;r[cnt].nex=head[y];r[cnt].val=0;head[y]=cnt;
}
I void BFS()
{
qwq.push(t);dis[t]=0;now[t]=head[t];gap[0]=1;
while(qwq.size())
{
LL x=qwq.front();qwq.pop();
for(R LL i=head[x];i;i=r[i].nex)
{
if(!dis[r[i].to] and r[i].to!=t)
{
LL y=r[i].to;
qwq.push(y);
now[y]=head[y];
dis[y]=dis[x]+1;
++gap[dis[y]];
}
}
}
}
LL ISAP(LL x,LL flow)
{
if(x==t) Heriko flow;
LL rst=flow,k,i,y,mindis=inf;
for(i=now[x];i and rst;i=r[i].nex)
{
y=r[i].to;
if(r[i].val>0)
{
mindis=Hmin(mindis,dis[y]);
if(dis[y]+1==dis[x])
{
k=ISAP(y,Hmin(rst,r[i].val));
r[i].val-=k;
r[i^1].val+=k;
rst-=k;
}
}
}
if(!rst) Heriko flow;
--gap[dis[x]];
if(!gap[dis[x]]) dis[s]=n+1;
if(mindis==inf) dis[x]=n;
else dis[x]=mindis+1;
++gap[dis[x]];
Heriko flow-rst;
}
LL z,y,x,flow;
S main()
{
fr(n),fr(m),fr(s),fr(t);
for(R LL i=1;i<=m;++i)
{
fr(z),fr(y),fr(x);
Ass_we_can(z,y,x);
}
BFS();
while(dis[s]<n)
{
for(R LL i=1;i<=n;++i) now[i]=head[i];
maxflow+=ISAP(s,inf);
}
fw(maxflow,1);
Heriko Deltana;
}
复杂度相关
ISAP 和 Dinic 的时间复杂度上界是一样的,都是 \(O(n^2m)\),但是这个上界相对宽松,平均情况下达不到这个复杂度。
End
芜湖(
参考资料
- [1] 究级的最大流算法:ISAP与HLPP —— 钱逸凡