天天看點

最大流算法之ISAP

序:

在之前的博文中,我解釋了關于最大流的EK與Dinic算法,以及它們的STL/非STL的實作(其實沒什麼差別)。本次講解的是ISAP算法。‘I’,指 improved,也就是說ISAP其實是SAP算法的改進。目前沒有官方名稱。

經過測試,ISAP的效率在洛谷的闆子題中遠勝于EK和Dinic的,速度大概是它們的2-3倍。代碼量實際上并沒有多大變化,在20行讀入優化與不壓行的情況下(即下文代碼),200-210行。(如果壓行的話,120行問題不大。不要問我為什麼,我空行太多…如果加讀入優化的話,再加上15行)

本文給出三種實作方式:遞歸,非遞歸,STL,非STL,将ISAP用結構體封裝,直接調用函數(每種包含幾個,實際上并沒有什麼差别)。

溫馨提示:

在開啟O2優化後,vector+queue版的ISAP比數組模拟的要快一點點(10ms?,運作時間均在90-110ms之間)。

如果不開優化,數組模拟比STL版要快2-3倍,達到120-150ms,而STL版是350-400ms(多次送出測試)。

什麼級别的考試用什麼,這應該很好取舍。

ISAP的思路:

當我們使用Dinic跑最大流的時候,我們多次進行了BFS分層,直到彙點不在殘餘網絡之中。而ISAP則隻BFS分層一次(事實上你都甚至可以不進行分層,也就比分層慢一點)。ISAP通過新的方式分層。

對于任意一個節點x,當跑完數次最大流之後,我們發現所有與它相連的節點都不滿足level[i]+1 == level[x]了。出現這種情況,Dinic的做法是重新分層,而ISAP的做法是将level[x]置成min(level[i])+1,進而構造出一條可行流。

當然,這是有邊界的。我們從彙點開始分層,源點的層數最高是n-1(一條鍊),是以當level[st] >= n的時候,算法結束,殘餘網絡中一定不存在可行流了。

這種方式也就解釋了為什麼可以不預先分層。因為這樣,分層的工作就留給了每一次推進可行流之中(否則無法找到可行流)。

注:分層時從彙點分層。

但是如果僅僅是這樣,實際上快不了多少。

真正的優化在于這兩個:gap優化和目前弧(cur)優化。

gap優化:

Dinic的反複分層被改成了ISAP的向上構造,而可行流必須滿足level[起點] = level[終點]+1,也就是說如果出現了斷層,也就是說在某一level的層級中不存在節點,那麼就不會再出現可行流(比如這一層是x,那麼流無法從lev.(x-1)流到lev.(x+1)),直接結束算法,比不加它的ISAP**快到不知哪裡去了**。

cur優化(目前弧):

對于任意一個節點x,假設最大流已經經過它流向下一個節點,而後在某次又流到了它(比如說已經找到一條可行流,從源點重新尋找可行流,再次到達該節點),那麼該可行流一定不能再流到之前流過的那些弧了(那些弧已經流過了,再進去相當于又走了一遍老路,但是上一次已經最大化的利用了它的流量限制,也就是說這次是沒有任何意義的行為。)

為了避免這種情況,我們用一個數組cur[]來記錄每個節點目前流到的弧的編号(這是邊表存儲的情況。如果是鄰接矩陣存儲,那就記錄目前流到了下一個節點的編号),當下一次又經過該節點的時候,我們隻需要再去嘗試沒有流過的弧了。

舉個例子:

節點5有三條弧,分别是arc1,arc2,arc3 。在之前尋找可行流的過程中,經過了arc1。那麼将cur[5] = arc2,下一次便從arc2開始尋找可行流。

可能會有這樣一個問題:

憑什麼就說arc1就沒有再走的價值了,那如果上一次通過arc1流向了節點6,而可行流最終是通過節點6的某一條弧流向彙點的,但是節點6不一定隻有一條弧啊,下一次還可以走節點6的其他弧。

解釋也很簡單,這是遞歸的過程,如果流已經流回到了節點5,那麼說明節點6以及arc1所抵達的所有節點都已經進行了這項操作。

當然,如果出現對某結點重新分層的時候,要将該節點的cur置回0(因為目前已經到尾了)。

代碼實作:

1.STL+DFS+非結構體版:

/*
About: Max_flow ISAP STL+DFS 
Auther: kongse_qi
Date: 2017/04/30
*/

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <cstring> 
#include <ctype.h>
#include <queue>

#define maxn 10005
#define maxm 200005
#define INF 0x3f3f3f

#define read(x) Get_Int(), x = in
#define p_b(x) push_back(x)

using namespace std;

struct Edge{
    int st, en, weight;
    Edge(){}
    Edge(int s, int e, int w):
        st(s), en(e), weight(w){}
}edge[maxm];

vector<int> arc[maxn];  
typedef vector<int>::iterator iterator_t;

int n, m, st, en, tot;
int max_flow;
int num[maxn];  
int dis[maxn];
int cur[maxn];
int pre[maxn];

char *X, *Buffer, c;
int in;

void Get_All()
{
    fseek(stdin, , SEEK_END);
    int file_lenth = ftell(stdin);
    rewind(stdin);
    Buffer = (char*)malloc(file_lenth*sizeof(char));
    fread(Buffer, , file_lenth, stdin);
    X = Buffer;
    c = *X;
    return ;
}

void Get_Int()
{
    in = ;
    while(!isdigit(c))  c = *++X;
    while(isdigit(c))
    {
        in = in*+c-'0';
        c = *++X;
    }
    return ;
}

void Init()
{
    //freopen("test.in", "r", stdin);
    Get_All();
    tot = -;
    read(n), read(m);
    read(st), read(en); 
    int a, b, c;
    for(int i = ; i != m; ++i)
    {
        read(a), read(b), read(c);
        edge[++tot] = Edge(a, b, c);
        edge[++tot] = Edge(b, a, );
        arc[a].p_b(tot-);
        arc[b].p_b(tot);
    }
    return ;
}

bool Bfs()
{
    bool wh[maxn];
    memset(wh, , sizeof wh);
    queue<int> q;
    q.push(en);
    wh[en] = true;
    while(!q.empty())
    {
        int cur = q.front(); q.pop();
        for(iterator_t i = arc[cur].begin(); i != arc[cur].end(); ++i)
        {
            Edge& ne = edge[*i];
            if(wh[ne.en] == false)
            {
                dis[ne.en] = dis[cur]+;
                q.push(ne.en);
                wh[ne.en] = true;
            }
        }
    }
    if(wh[st] == false) return false;
    return true;
}

int Augment(int x)
{
    int minn = INF;
    while(x != st)
    {
        Edge& e = edge[pre[x]];
        minn = min(minn, e.weight);
        x = e.st;
    }
    x = en;
    while(x != st)
    {
        int curr = pre[x];
        edge[curr].weight -= minn;
        edge[curr^].weight += minn;
        x = edge[curr].st;
    }
    return minn;
}

void Advance(int x)
{
    bool wh = false;
    for(int& i = cur[x]; i != arc[x].size(); ++i)
    {
        Edge& e = edge[arc[x][i]];
        if(e.weight >  && dis[x] == dis[e.en]+)
        {
            wh = true;
            pre[e.en] = arc[x][i];
            x = e.en;
            break;
        }
    }
    if(wh == false)
    {
        int m = n-;
        for(iterator_t i = arc[x].begin(); i != arc[x].end(); ++i)
        {
            Edge& e = edge[*i];
            if(e.weight > )
            {
                m = min(m, dis[e.en]);
            }
        }
        if(--num[dis[x]] == )  return ;
        ++num[dis[x] = m+];
        cur[x] = ;
        if(x != st) x = edge[pre[x]].st;
    }
    if(x == en) 
    {
        max_flow += Augment(en);
        x = st;
    }
    if(dis[st] >= n)    return ;
    Advance(x);
    return ;
}

int Isap()
{
    if(Bfs())
    {
        for(int i = ; i != n+; ++i)
        {
            ++num[dis[i]];
        }
        Advance(st);
    }
    else return -;
    return max_flow;
}

int main()
{
    Init();
    printf("%d\n", Isap());
    return ;
}
           

2.struct+非STL+BFS版 (不開O2它最快)

/*
About: Max_flow ISAP struct 非STL+BFS 
Auther: kongse_qi
Date: 2017/04/30
*/

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring> 
#include <ctype.h>

#define maxn 10005
#define maxm 200005
#define INF 0x3f3f3f

#define read(x) Get_Int(), x = in

using namespace std;

void Get_Int();
void Get_All();

char *X, *Buffer, c;
int in;

struct Edge{
    int st, en, weight;
    Edge(){}
    Edge(int s, int e, int w):
        st(s), en(e), weight(w){}
};

struct Isap{
    int st, en, tot, n, m;
    int max_flow;
    int num[maxn];  
    int dis[maxn];
    int cur[maxn];
    int pre[maxn];
    int arc[maxn][maxn/];
    int amount[maxn];
    Edge edge[maxm];

    void Init(int st, int en, int n, int m)
    {
        tot = -;
        this -> st = st; this -> en = en;
        this -> n = n; this -> m = m;
        max_flow = ;
        memset(cur, , sizeof cur);
        memset(num, , sizeof num);
        memset(amount, -, sizeof amount);
        return ;
    }

    void Add_edge(int s, int e, int w)
    {
        edge[++tot] = Edge(s, e, w);
        edge[++tot] = Edge(e, s, );
        arc[s][++amount[s]] = tot-;
        arc[e][++amount[e]] = tot;
        return ;
    }

    bool Bfs()
    {
        bool wh[maxn];
        memset(wh, , sizeof wh);
        int q[maxn], st_pos = -, en_pos = -;
        q[++en_pos] = en;
        wh[en] = true;
        while(st_pos != en_pos)
        {
            int cur = q[++st_pos];
            for(int i = ; i != amount[cur]+; ++i)
            {
                Edge& ne = edge[arc[cur][i]];
                if(wh[ne.en] == false)
                {
                    dis[ne.en] = dis[cur]+;
                    q[++en_pos] = ne.en;
                    wh[ne.en] = true;
                }
            }
        }
        if(wh[st] == false) return false;
        return true;
    }

    int Augment(int x)
    {
        int minn = INF;
        while(x != st)
        {
            Edge& e = edge[pre[x]];
            minn = min(minn, e.weight);
            x = e.st;
        }
        x = en;
        while(x != st)
        {
            int curr = pre[x];
            edge[curr].weight -= minn;
            edge[curr^].weight += minn;
            x = edge[curr].st;
        }
        return minn;
    }

    void Advance(int x)
    {
        while(dis[st] < n)
        {
            bool wh = false;
            for(int& i = cur[x]; i != amount[x]+; ++i)
            {
                Edge& e = edge[arc[x][i]];
                if(e.weight >  && dis[x] == dis[e.en]+)
                {
                    wh = true;
                    pre[e.en] = arc[x][i];
                    x = e.en;
                    break;
                }
            }
            if(wh == false)
            {
                int m = n-;
                for(int i = ; i != amount[x]+; ++i)
                {
                    Edge& e = edge[arc[x][i]];
                    if(e.weight > )
                    {
                        m = min(m, dis[e.en]);
                    }
                }
                if(--num[dis[x]] == )  return ;
                ++num[dis[x] = m+];
                cur[x] = ;
                if(x != st) x = edge[pre[x]].st;
            }
            if(x == en) 
            {
                max_flow += Augment(en);
                x = st;
            }
        }
        return ;
    }

    int Max_flow()
    {
        Bfs();
        for(int i = ; i != n+; ++i)
        {
            ++num[dis[i]];
        }
        Advance(st);
        return max_flow;
    }
}doit;

int main()
{
    int a, b, c, n, m, st, en;
    freopen("test.in", "r", stdin);
    Get_All();
    read(n), read(m);
    read(st), read(en); 
    doit.Init(st, en, n, m);
    for(int i = ; i != m; ++i)
    {
        read(a), read(b), read(c);
        doit.Add_edge(a, b, c);
    }
    printf("%d\n", doit.Max_flow());
    return ;
}

void Get_All()
{
    fseek(stdin, , SEEK_END);
    int file_lenth = ftell(stdin);
    rewind(stdin);
    Buffer = (char*)malloc(file_lenth*sizeof(char));
    fread(Buffer, , file_lenth, stdin);
    X = Buffer;
    c = *X;
    return ;
}

void Get_Int()
{
    in = ;
    while(!isdigit(c))  c = *++X;
    while(isdigit(c))
    {
        in = in*+c-'0';
        c = *++X;
    }
    return ;
}
           

3 struct+STL+BFS版

/*
About: Max_flow ISAP struct STL+BFS 
Auther: kongse_qi
Date: 2017/04/30
*/

#pragma GCC optimize(3)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <cstring> 
#include <ctype.h>
#include <queue>

#define maxn 10005
#define maxm 200005
#define INF 0x3f3f3f

#define read(x) Get_Int(), x = in
#define p_b(x) push_back(x)

using namespace std;

void Get_Int();
void Get_All();

char *X, *Buffer, c;
int in;

typedef vector<int>::iterator iterator_t;

struct Edge{
      int st, en, weight;
      Edge(){}
    Edge(int s, int e, int w):
        st(s), en(e), weight(w){}
};

struct Isap{
    int st, en, tot, n, m;
    int max_flow;
    int num[maxn];  
    int dis[maxn];
    int cur[maxn];
    int pre[maxn];
    vector<int> arc[maxn];  
    Edge edge[maxm];

    void Init(int st, int en, int n, int m)
    {
        tot = -;
        this -> st = st; this -> en = en;
        this -> n = n; this -> m = m;
        for(int i = ; i != n+; ++i)
        {
            arc[i].clear();
        }
        max_flow = ;
        memset(cur, , sizeof cur);
        memset(num, , sizeof num);
        return ;
    }

    void Add_edge(int s, int e, int w)
    {
        edge[++tot] = Edge(s, e, w);
        edge[++tot] = Edge(e, s, );
        arc[s].p_b(tot-);
        arc[e].p_b(tot);
        return ;
    }

    bool Bfs()
    {
        bool wh[maxn];
        memset(wh, , sizeof wh);
        queue<int> q;
        q.push(en);
        wh[en] = true;
        while(!q.empty())
        {
            int cur = q.front(); q.pop();
            for(iterator_t i = arc[cur].begin(); i != arc[cur].end(); ++i)
            {
                Edge& ne = edge[*i];
                if(wh[ne.en] == false)
                {
                    dis[ne.en] = dis[cur]+;
                    q.push(ne.en);
                    wh[ne.en] = true;
                }
            }
        }
        if(wh[st] == false)    return false;
        return true;
    }

    int Augment(int x)
    {
        int minn = INF;
        while(x != st)
        {
            Edge& e = edge[pre[x]];
            minn = min(minn, e.weight);
            x = e.st;
        }
        x = en;
        while(x != st)
        {
            int curr = pre[x];
            edge[curr].weight -= minn;
            edge[curr^].weight += minn;
            x = edge[curr].st;
        }
        return minn;
    }

    void Advance(int x)
    {
        while(dis[st] < n)
        {
            bool wh = false;
            for(int& i = cur[x]; i != arc[x].size(); ++i)
            {
                Edge& e = edge[arc[x][i]];
                if(e.weight >  && dis[x] == dis[e.en]+)
                {
                    wh = true;
                    pre[e.en] = arc[x][i];
                    x = e.en;
                    break;
                }
            }
            if(wh == false)
            {
                int m = n-;
                for(iterator_t i = arc[x].begin(); i != arc[x].end(); ++i)
                {
                    Edge& e = edge[*i];
                    if(e.weight > )
                    {
                        m = min(m, dis[e.en]);
                    }
                }
                if(--num[dis[x]] == )    return ;
                ++num[dis[x] = m+];
                cur[x] = ;
                if(x != st)    x = edge[pre[x]].st;
            }
            if(x == en)    
            {
                max_flow += Augment(en);
                x = st;
            }
        }
        return ;
    }

    int Max_flow()
    {
        Bfs(); 
        for(int i = ; i != n+; ++i)
        {
            ++num[dis[i]];
        } 
        Advance(st);
        return max_flow;
    }
}doit;

int main()
{
    int a, b, c, n, m, st, en;
    //freopen("test.in", "r", stdin);
    Get_All();
    read(n), read(m);
    read(st), read(en); 
    doit.Init(st, en, n, m);
    for(int i = ; i != m; ++i)
    {
        read(a), read(b), read(c);
        doit.Add_edge(a, b, c);
    }
    printf("%d\n", doit.Max_flow());
    return ;
}

void Get_All()
{
    fseek(stdin, , SEEK_END);
    int file_lenth = ftell(stdin);
    rewind(stdin);
    Buffer = (char*)malloc(file_lenth*sizeof(char));
    fread(Buffer, , file_lenth, stdin);
    X = Buffer;
    c = *X;
    return ;
}

void Get_Int()
{
    in = ;
    while(!isdigit(c))    c = *++X;
    while(isdigit(c))
    {
        in = in*+c-'0';
        c = *++X;
    }
    return ;
}
           

結構體版實際上就是将函數作為結構體的成員函數運作,在主函數中調用時邏輯會更加清晰(分區很明顯)。

實測結果:

O2優化情況下:

1.耗時:115ms

記憶體:17281kb

2.耗時:80ms

記憶體:23304kb

3.耗時:103ms

記憶體:17281kb

其實差距不大。

不開O2的情況下:

1.耗時:322ms

記憶體:17429kb

2.耗時:141ms

記憶體:23304kb

3.耗時:323ms

記憶體:17292kb

(結果不一定十分穩定)

是以不開優化的STL是非常不建議使用的。

STL的快速與便捷隻有在開啟優化的情況下才能兼得。

附壓行代碼:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring> 
#include <ctype.h>
#include <vector>
#define maxn 10005
#define maxm 200005
#define INF 0x3f3f3f
using namespace std;
struct Edge{
    int st, en, weight;
    Edge(){}
    Edge(int s, int e, int w):
        st(s), en(e), weight(w){}
};
struct Isap{
    int st, en, tot, n, m, max_flow;
    int num[maxn], dis[maxn], cur[maxn], pre[maxn], arc[maxn][], amount[maxn];
    vector<Edge> edge;
    void Init(int st, int en, int n, int m){
        tot = -;
        this -> st = st; this -> en = en;
        this -> n = n; this -> m = m;
        max_flow = ;
        edge.resize(m*);
        memset(cur, , sizeof cur);
        memset(num, , sizeof num);
        memset(amount, -, sizeof amount);
    }
    void Add_edge(int s, int e, int w){
        edge[++tot] = Edge(s, e, w);
        edge[++tot] = Edge(e, s, );
        arc[s][++amount[s]] = tot-;
        arc[e][++amount[e]] = tot;
    }
    void Bfs(){
        bool wh[maxn] = {};
        int q[maxn], st_pos = -, en_pos = -;
        q[++en_pos] = en;
        wh[en] = true;
        while(st_pos != en_pos){
            int cur = q[++st_pos];
            for(int i = ; i != amount[cur]+; ++i){
                Edge ne = edge[arc[cur][i]];
                if(wh[ne.en] == false){
                    dis[ne.en] = dis[cur]+;
                    q[++en_pos] = ne.en;
                    wh[ne.en] = true;
                }
            }
        }
    }
    int Augment(){
        int minn = INF, curr;
        Edge e;
        for(int x = en; x != st; ){
            e = edge[pre[x]];
            minn = min(minn, e.weight);
            x = e.st;
        }
        for(int x = en; x != st;){
            curr = pre[x];
            edge[curr].weight -= minn;
            edge[curr^].weight += minn;
            x = edge[curr].st;
        }
        return minn;
    }
    void Advance(int x){
        while(dis[st] < n){
            bool wh = false;
            for(int& i = cur[x]; i != amount[x]+; ++i){
                Edge &e = edge[arc[x][i]];
                if(e.weight >  && dis[x] == dis[e.en]+){
                    wh = true;
                    pre[e.en] = arc[x][i];
                    x = e.en;
                    break;
                }
            }
            if(wh == false){
                int minn = n-;
                for(int i = ; i != amount[x]+; ++i){
                    Edge& e = edge[arc[x][i]];
                    if(e.weight > )
                        minn = min(minn, dis[e.en]);
                }
                if(--num[dis[x]] == )  return ;
                ++num[dis[x] = minn+];
                cur[x] = ;
                if(x != st) x = edge[pre[x]].st;
            }
            if(x == en) {
                max_flow += Augment();
                x = st;
            }
        }
    }
    int Max_flow(){
        Bfs();
        for(int i = ; i != n+; ++i)
            ++num[dis[i]];
        Advance(st);
        return max_flow;
    }
}doit;
int main(){
    int a, b, c, n, m, st, en;
    //freopen("test.in", "r", stdin);
    scanf("%d%d", &n, &m);
    st = , en = n;
    doit.Init(st, en, n, m);
    for(int i = ; i != m; ++i){
        scanf("%d%d%d", &a, &b, &c);
        doit.Add_edge(a, b, c);
    }
    printf("%d\n", doit.Max_flow());
    return ;
}
           

(121行total)

自此結束。

箜瑟_qi 2017.04.30 23:20

四月最後一篇。

繼續閱讀