网络流——ISAP

我数次想要写网络流的博客,但是东西太多了,我又太颓了,以至于一直没有写……

这次就写一个ISAP吧!学了ISAP和(自己的)Dinic一比,感觉还是快了许多的!(不是和loj上前几名的大佬的Dinic比)

最大流还有一点要吐槽的,就是当前弧优化不知道为什么写不对(每次加过之后Dinic就会变慢……),所以一直就没加当前弧优化……

由于本人太懒了,不想写一遍思路,具体请参见注释。

注:代码注释中"和Dinic相同"之类的字眼,不用担心,我之后会加一篇Dinic的博客。

#include<cstdio>
#include<cstring>
#define min(a,b) (a<b?a:b)
#define MXN 10000+2
#define MXM 100000+10
int v[2*MXM],flow[2*MXM],fst[MXN],nxt[2*MXM];   //----邻接表 
int layer[MXN],gap[MXN];               //----网络图信息:layer-层、gap-优化数组 
int etop,S,T,n;                //----etop边数,S源点,T汇点,n点数 
int queue[MXN+100],head,tail;//----手打队列 
void AddEdge(int x,int y,int z){
    //----加边函数:每次连续加正反两条边
    //----从0开始加边,可以用位运算瞎搞,第i条边的反向边为第i^1条边 
    v[etop]=y;
    flow[etop]=z;
    nxt[etop]=fst[x];
    fst[x]=etop++;
    v[etop]=x;
    flow[etop]=0;
    nxt[etop]=fst[y];
    fst[y]=etop++;
    return;
}
void GraphInit(){
    //----建图(残量网络) 
    memset(fst,-1,sizeof(fst));
    memset(nxt,-1,sizeof(nxt));
    int m,x,y,z;
    scanf("%d%d%d%d",&n,&m,&S,&T);
    etop=0;
    for(int i=0;i<m;i++){
        scanf("%d%d%d",&x,&y,&z);
        AddEdge(x,y,z);
    }
    return;
}
void ISAPInit(){
    //----ISAP初始化,上来从汇点bfs分一下层,用于之后dfs
    //----同时加gap优化 
    memset(layer,-1,sizeof(layer));
    memset(gap,0,sizeof(gap));
    queue[tail++]=T;
    layer[T]=0;
    gap[layer[T]]++;
    while(head<tail){
        int x=queue[head++];
        for(int y=fst[x];y!=-1;y=nxt[y]){
            if(layer[v[y]]==-1){
                layer[v[y]]=layer[x]+1;
                gap[layer[v[y]]]++;
                queue[tail++]=v[y];
            }
        }
    }
    return;
}
int ISAPDfs(int now,int f){
    //----ISAP求解最大流的主要函数
    //----Dfs到汇点直接返回
    if(now==T) return f;
    int sum=0,temp;
    for(int y=fst[now];y!=-1;y=nxt[y]){
        //----和Dinic的Dfs相同
        if(layer[v[y]]+1==layer[now]){
            temp=ISAPDfs(v[y],min(flow[y],f));
            sum+=temp;
            f-=temp;
            flow[y]-=temp;
            flow[y^1]+=temp;
            if(f==0) return sum;
        }
    }
    //----gap优化的核心,也算是ISAP很快的主要原因
    //----gap记录每一层的点数 
    if(--gap[layer[now]]==0) layer[S]=n;
    ++gap[++layer[now]];
    //----首先,这次Dfs后,这一个点满足分层条件的所有边都已经增广
    //----因此这个点已经不用在这一层待着了,但是由于我们不知道他可以到哪一层,将层数++,下次Dfs再试
    //----同时将原层数的gap--。优化在于,如果减后gap==0,那么就会出现断层,之后就没有满足分层的增广路了
    //----就可以不用ISAP了,就将S的layer置为n 
    return sum;
}
int ISAP(){
    int ans=0;
    ISAPInit();
    while(layer[S]<n) ans+=ISAPDfs(S,0x7fffffff);
    //----S的层是n的话,表示一定出现了断层,或者已经没有从S出发的增广路了(即进行过layer[S]++)
    //----因此不能再增广了,一定达到了最大流,退出ISAP即可。 
    return ans;
}
int main(){
    GraphInit();
    printf("%d",ISAP());
    return 0;
}
ISAP

代码交到洛谷模板题上可AC。

posted @ 2018-02-02 10:09  Halifuda  阅读(316)  评论(0编辑  收藏  举报