網絡流

最大流:

最大流是網絡流的一種,他表示從起始點s出發,引出無窮的流量,每條邊上允許通過一個最大的流量,求最終能夠到達目的地的最大流量是多少。

由於還是在圖上進行操作,因此最大流的求解方法依然是基於dfs或者bfs的做法。故網絡流比較耗費時間,常常所需要的點數不能夠過大,大概在1k左右。

最大流的一般方法:

1.Ford_Fulkerson算法

從源點s開始,不斷進行dfs遍歷,尋找到一條能夠走到t的道路,記錄這條道路上的最小邊的值,將這條路上所有道路的cap減去該值,並將這些道路rev道路上的的cap加上對應值。

每次dfs返回這個最小值,用一個大大的while循環去不斷進行dfs,在dfs的參數中保存當前所在結點,目的結點,以及能夠到達當前節點的流量的最小值。

目的結點用於判定dfs結束,當前所在結點用於dfs向下遍歷,採用used數組存儲已經訪問的點。

複雜度:o(F|E|);F表示最大流量

struct edge
{
    int to;
    int cap;
    int rev;
    edge(){}
    edge(int a,int b,int c)
    {
        to=a;cap=b;rev=c;
    }
};

vector<edge> g[maxn];
bool used[maxn];

//單向流通!!!!
void add_edge(int f,int t,int c)
{
    g[f].push_back(edge(t,c,g[t].size()));
    g[t].push_back(edge(f,0,g[f].size()-1));
}

int dfs(int v,int t,int f)
{
    if(v==t)
        return f;
    used[v]=true;

    int len=g[v].size();
    for(int i=0;i<len;i++)
    {
        edge &e=g[v][i];
        if(used[e.to]==false && e.cap>0)
        {
            int d=dfs(e.to,t,min(f,e.cap));
            if(d>0)
            {
                e.cap=e.cap-d;
                g[e.to][e.rev].cap+=d;
                return d;
            }
        }
    }
    return 0;
}

int maxflow(int s,int t)
{
    int flow=0;
    while(true)
    {
        memset(used,0,sizeof(used));
        int f=dfs(s,t,inf);
        if(f==0)
            break;
        flow=flow+f;
    }
    return flow;
}

 

2.dinic算法:

一種比較優秀的基於bfs與dfs結合的網絡流最大流算法,常常作爲模版算法

複雜度:o(|E|V^2);

struct edge
{
    int to;
    int cap;
    int rev;
    edge(){}
    edge(int a,int b,int c){to=a;cap=b;rev=c;}
};

vector<edge> g[maxn];
int level[maxn];  //頂點到原點的距離編號
int iter[maxn];   //當前弧,在其之前的都沒用了

//單向流通!!!!
void add_edge(int f,int t,int c)
{
    g[f].push_back(edge(t,c,g[t].size()));
    g[t].push_back(edge(f,0,g[f].size()-1));
}

//先用bfs爲每一個有價值的點計算到s點距離標號
void bfs(int s)
{
    memset(level,-1,sizeof(level));
    queue<int> que;
    while(!que.empty())
        que.pop();

    level[s]=0;
    que.push(s);
    while(!que.empty())
    {
        int u=que.front();
        que.pop();
        int len=g[u].size();
        for(int i=0;i<len;i++)
        {
            edge &e=g[u][i];
            if(e.cap>0 && level[e.to]<0)
            {
                level[e.to]=level[u]+1;
                que.push(e.to);
            }
        }
    }
}

int dfs(int v,int t,int f)
{
    if(v==t)
        return f;

    int len=g[v].size();
    for(int &i=iter[v];i<len;i++) //當前弧優化的方法,一種思想
    {
        edge &e=g[v][i];
        if(e.cap>0 && level[v]<level[e.to])
        {
            int d=dfs(e.to,t,min(f,e.cap));
            if(d>0)
            {
                e.cap=e.cap-d;
                g[e.to][e.rev].cap+=d;
                return d;
            }
        }
    }
    return 0;
}

int maxflow(int s,int t)
{
    int flow=0;
    while(true)
    {
        bfs(s);
        if(level[t]<0)
            break;

        memset(iter,0,sizeof(iter));
        int f;
        while((f=dfs(s,t,inf))>0 )
            flow=flow+f;
    }
    return flow;
}

 

優化技巧

縮點:

在實際情況中,我們常常會遇到一些問題,使得有的點的規模比較大,比如{a1,a2....an}與{b1,b2......bn}相互連接形成的二分圖中,我們發現a的規模達到了極其高,b的規模很小隻有10左右。

我們就發現這樣一個事實,對於兩個不同的ai和aj來說,他們連接到b的方式很可能完全相同(一定要相同才行),我們將其完全獨立進行流量算法的時候會計算兩次,但是如果我們將他們合併成一個點,對應邊上流量通過量求和,我們就可以將點縮成一個點。因此可以極大的減少運算的複雜度。

對b的處理採用二進制位處理進行判斷和連接對應邊

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章