初见 | 图论 | 网络流 | 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 的过程

  1. 从汇点 t 到源点 s 跑一次 BFS 进行深度标记

  2. 从源点 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

芜湖(

参考资料

posted @ 2021-07-06 19:34  HerikoDeltana  阅读(126)  评论(2编辑  收藏  举报