网络流学习笔记2

网络流(网络最大流)学习笔记2

上次写了那个网络流笔记1,是关于FF算法的。

这次来一发拓展——EK算法Dinic算法

I-sap 先咕咕咕算了

Emdond-Karp 算法

Edmond-Karp算法(下简称EK算法)是对FF算法的bfs优化。

原先dfs算起来有可能搜到好几次,但是广搜对于判重有着天然的优势。

所以bfs优化以后时间复杂度(\(O(VE^2)\))反而小了。

需要用到的是残量网络,即可行流流过去剩下的可以流的大小。

先放一个邻接矩阵的(Luogu【模板】网络最大流 上面MLE了三个点 70pts)

#include <iostream>
#include <cstdio>
#include <queue>

using namespace std;
const int INF=2100000000,maxn=10001;
int left_flow[maxn][maxn];
int pre[maxn];
int flow[maxn];
int n,m;

inline int bfs(int S,int T)
{
    queue<int>q;
    for (register int i=1; i<=n; i++)
    {
        pre[i]=-1;
    }
    pre[S]=0;
    flow[S]=INF;
    q.push(S);
    while (!q.empty())
    {
        int from=q.front();
        q.pop();
        if (from==T)
            break;
        for (register int i=1; i<=n; i++)
        {
            if (i!=S && left_flow[from][i]>0 && pre[i]==-1)
            {
                pre[i]=from;
                flow[i]=min(flow[from],left_flow[from][i]);
                q.push(i);
            }
        }
    }
    if (pre[T]==-1)
        return -1;
    else
        return flow[T];
}

inline int EK(int S,int T)
{
    int canflow=0,nowflow=0;
    while (canflow=bfs(S,T),canflow!=-1)
    {
        int now=T;
        while(now!=S)
        {
            int from=pre[now];
            left_flow[from][now]-=canflow;
            left_flow[now][from]+=canflow;
            now=from;
        }
        nowflow+=canflow;
    }
    return nowflow;
}

int main()
{
    int S,T;
    scanf("%d%d%d%d",&n,&m,&S,&T);
    for (register int i=1; i<=m; i++)
    {
        int f,t,v;
        scanf("%d%d%d",&f,&t,&v);
        if (f==t)
            continue;
        left_flow[f][t]+=v;
    }
    printf("%d\n",EK(S,T));
    return 0;
}

因为邻接矩阵实在是效率太低啦

所以我们换成链式前向星,保证只遍历出边。

#include <iostream>
#include <cstdio>
#include <queue>

using namespace std;
const int INF=2100000000,maxn=10001;
struct edge{
    int from,to,rev,next,left_flow;
};
edge Edge[200001];
int cnt=1,head[10001];
inline void add_edge(int from,int to,int flow)
{
    //正向边 
          Edge[cnt].from=from,
              Edge[cnt].to=to,
     Edge[cnt].left_flow=flow,
    Edge[cnt].next=head[from],
          Edge[cnt].rev=cnt+1;
    head[from]=cnt++;
    //反边 
            Edge[cnt].from=to,
            Edge[cnt].to=from,
        Edge[cnt].left_flow=0,
      Edge[cnt].next=head[to],
          Edge[cnt].rev=cnt-1;
    head[to]=cnt++;
}
int pre[maxn],flow[maxn],n,m;

inline int bfs(int S,int T)
{
    queue<int> q;
    for (register int i=1;i<=n;i++)
        pre[i]=-1;
    flow[S]=INF;
    pre[S]=0;
    q.push(S);
    while (!q.empty())
    {
        int from=q.front();
        q.pop();
        if (from==T)break;
        for (register int i=head[from];i;i=Edge[i].next)
        {
            int to=Edge[i].to;
            if (Edge[i].left_flow>0 && pre[to]==-1)
            {
                pre[to]=i;
                flow[to]=min(flow[from],Edge[i].left_flow);
                q.push(to);
            }
        }
    }
    if (pre[T]==-1)
        return -1;
    else return flow[T];
}

inline int EK(int S,int T)
{
    int canflow=0,maxflow=0;
    while (canflow=bfs(S,T),canflow!=-1)
    {
        int now=T;
        while (now!=S)
        {
            Edge[pre[now]].left_flow-=canflow;
            Edge[Edge[pre[now]].rev].left_flow+=canflow;
            now=Edge[pre[now]].from;
        }
        maxflow+=canflow;
    }
    return maxflow;
}

int main()
{
    int S,T;
    scanf("%d%d%d%d",&n,&m,&S,&T);
    for (register int i=1;i<=m;i++)
    {
        int f,t,v;
        scanf("%d%d%d",&f,&t,&v);
        if (f==t)continue;
        add_edge(f,t,v);
    }
    printf("%d\n",EK(S,T));
    return 0;
}

(话说我为了27行眼瞎直接调了一晚上

Dinic 算法

Magic!将dfs和bfs相结合,分层之后时间复杂度下来了。

好像在一般情况下,变成了\(O(V^2E)\),而一般\(V\leq E\),所以Dinic优化的不是一点半点。

(下一个系列再说吧QAQ

posted @ 2019-06-15 20:44  小金羊  阅读(128)  评论(0编辑  收藏  举报