网络流详细总结

EK算法求最大流

  $O(nm^2)$

inline void add(int a, int b, int c)
{
    edge.pb(Edge(a, b, c, 0));
    edge.pb(Edge(b, a, 0, 0));
    int siz = edge.size();
    G[a].pb(siz - 2);
    G[b].pb(siz - 1);
}

inline int Maxflow(int sta, int ed)
{
    int res = 0;
    while (true)
    {
        queue<int> que;
        que.push(sta);
        memset(f, 0, sizeof(f));
        f[sta] = INF;
        while (que.size())
        {
            int u = que.front();
            que.pop();

            for (auto i : G[u])
            {
                Edge& e = edge[i];
                if (!f[e.b] && e.cap > e.flow)
                {
                    pre[e.b] = i;
                    f[e.b] = min(f[u], e.cap - e.flow);
                    que.push(e.b);
                }
            }
            if (f[ed]) break;
        }
        if (!f[ed]) break;

        res += f[ed];
        for (int i = ed; i != sta; i = edge[pre[i]].a)
        {
            edge[pre[i]].flow += f[ed]; 
            edge[pre[i] ^ 1].flow -= f[ed];
        }
    }
    return res;
}
View Code

Dinic算法求最大流

  $O(m\sqrt{n})$

inline void add(int a, int b, int c)
{
    edge.pb(Edge(a, b, c, 0));
    edge.pb(Edge(b, a, 0, 0));
    int siz = edge.size();
    G[a].pb(siz - 2);
    G[b].pb(siz - 1);
}

inline bool bfs(int sta, int ed)
{
    queue<int> que;
    que.push(sta);
    memset(dep, 0, sizeof(dep));
    memset(st, 0, sizeof(st));
    dep[sta] = 0;
    st[sta] = true;

    while (que.size())
    {
        int u = que.front();
        que.pop();

        for (auto i : G[u])
        {
            Edge e = edge[i];
            if (!st[e.b] && e.cap > e.flow)
            {
                dep[e.b] = dep[u] + 1;
                st[e.b] = true;
                que.push(e.b);
            }
        }
    }
    return st[ed];
}

int dfs(int u, int a, int ed)
{
    if (!a || u == ed) return a;
    int flow = 0, f = 0;
    for (int& i = cur[u]; i < G[u].size(); i++)
    {
        Edge& e = edge[G[u][i]];
        if (dep[e.b] == dep[u] + 1 && (f = dfs(e.b, min(a, e.cap - e.flow), ed)))
        {
            flow += f;
            e.flow += f;
            edge[G[u][i] ^ 1].flow -= f;
            a -= f;
            if (!a) break;
        }
    }
    return flow;
}

inline int Maxflow(int sta, int ed)
{
    int flow = 0;
    while (bfs(sta, ed))
    {
        memset(cur, 0, sizeof(cur));
        flow += dfs(sta, INF, ed);
    }
    return flow;
}
View Code

二分图匹配

  左边的点为$1~n$,右边的点为$n+1~n+m$

  若$i$点能与$j$点匹配,则连一条$i->j$的边,流量为$1$,从$S$连$S->i$的边,流量为$i$的权值,从$j$连$j->T$的边,流量为$j$的权值,则最大匹配为最大流

  例:[AcWing2175] [AcWing2179]

无源汇求上下界可行流

  每条边有流量上界和下界,问是否有一种方案使得所有点满足流量平衡的前提下,所有边满足流量限制

  设每条边下届为$low[i]$,上界为$upp[i]$

  则先给每条边$low[i]$的流量,之后设每个点流入的流量与流出的流量只差为$dout[i]$

  若$dout[i]>0$,说明$i$需要流量流入,则连一条$S->i$流量为$dout[i]$的边

  若$dout[i]<0$,说明$i$需要流量流出,则连一条$T->i$流量为$-dout[i]$的边

inline bool Dinic(int sta, int ed)
{
    int flow = 0;
    while (bfs(sta, ed))
    {
        memset(cur, 0, sizeof(cur));
        flow += dfs(sta, INF, ed);
    }
    return flow == num;
}

int mian()
{
    scanf("%d%d", &n, &m);
    int sta = n + m + 1, ed = n + m + 2;
    for (int i = 1; i <= m; i++)
    {
        scanf("%d%d%d%d", &a, &b, &low, &upp);
        low[i - 1] = low;
        add(a, b, upp - low);
        dout[a] -= low, dout[b] += low;
    }

    for (int i = 1; i <= n; i++)
    {
        if (dout[i] > 0) num += dout[i], add(sta, i, dout[i]);
        else add(i, ed, -dout[i]);
    }

    if (!Dinic(sta, ed)) puts("NO"), exit(0);
    puts("YES");
    for (int i = 0; i < m; i++) printf("%d\n", edge[i << 1 | 1].cap - edge[i << 1 | 1].flow + low[i]);
}
View Code

有源汇求上下界可行流

  给定有源汇流量网络$G$,起点为$S$,终点为$T$,询问是否存在一种标定每条边流量的方式,使得每条边流量满足上下界同时除了源点和汇点每一个点流量平衡。

  连一条$T->S$下届为$0$上界为$\infty $的边之后当无源汇上下界可行流

有源汇求上下界最大流

  求最大的可行流

  求出可行流后加上残量网络从$S$到$T$的最大流即为答案

有源汇求上下界最小流

  最小可行流

  其他的和上面一样,只是最后的答案为可行流减去残量网络$T$到$S$的最大流

 多源汇最大流

  从$S$向每个源点连一条容量为$\infty $的边,从每个汇点向$T$连一条容量为$\infty $的边,答案即为从$S$到$T$的最大流

关键边问题

  若增加某条边的容量之后整张图的最大流会增加,则该边是关键边

  跑一遍最大流后,边$c(u,v)$为关键边的条件是:

  $1、c(u,v)$满流

  $2、$在残量网络中,$S->u$和$v->T$都有路径(路径上的边容量大于$0$)

最小割

  最小割$=$最大流

  求方案数:在残量网络上从$s$开始$DFS$,只走残量大于$0$的边,经过的点属于$S$

  求割边数量:将每条边容量变为1,再跑一次最大流

最大权闭合子图

  闭合图:图中所有节点的出边的终点都在图内

  最大权闭合子图:求一个图中节点权值和最大的子图,且该子图是闭合图

  求最大权闭合子图:从$S$向所有权值为正的点连容量为点权的边,从所有权值为负的点向$T$连容量为点权的绝对值的边,答案就为所有正点权和减最小割

最大密度子图

  求一个图的子图$G=(E,V)$使得$\frac{|E|}{|V|}$最大

  二分$g$,设$g=(\frac{|E|}{|V|})_{max}$

  则$(\frac{|E|}{|V|})_{max}-g=0$

  $|E|-g*|V|=0$

  将其转换成最大权闭合子图,边的点权为$1$,点的点权为$-g$,求最大权闭合子图,答案$ans$便是$(|E|-g*|V|)_{max}$

  若$ans-g>0$,说明$g$还可以再大,否则就要减小,以此类推二分

  求方案数:在残量网络中从$S$只走没满流的边,经过的与$T$相连的点便是选中的点

最小点权覆盖集

  选出一些点使得每条边的两个端点至少有一个点被选到,且点权和最小

  给一条边的两端染黑白两色,使得每条边两个端点颜色都不同

  从$S$向每个黑点连容量为点权的边,从每个白点向$T$连容量为点权的边,答案即为最小割

  求方案:在残量网络上从$S$出发只走没满流的边,与$S$相连的没被经过的点和与$T$相连的被经过的点就为选中的点

最大点权独立集

  每条边最多被选择一个端点,且点权和最大

  最大点权独立集$-$点权和$-$最小点权覆盖集

费用流

  连边:若有一条$a->b$的边且流量为$c$费用为$d$,则连两条边$add(a,b,c,d),add(b,a,0,-d)$

  求最大流时,把找增广路的表示层数的$dep$改为表示最短路径的$dis$,找增广路的$bfs$用$spfa$来写最短路

  用$Dicin$实现 $O(n·m·flow_{max})$:

inline void add(int a, int b, int c, int d)
{
    num[dqx] = b;
    f[dqx] = c;
    cost[dqx] = d;
    nex[dqx] = h[a];
    h[a] = dqx++;

    num[dqx] = a;
    f[dqx] = 0;
    cost[dqx] = -d;
    nex[dqx] = h[b];
    h[b] = dqx++;
}

inline bool spfa(int S, int T)
{
    queue<int> que;
    que.push(S);
    memset(dis, 0x7f, sizeof(dis));
    memset(st, 0, sizeof(st));
    dis[S] = 0, st[S] = true;

    while (que.size())
    {
        int u = que.front();
        que.pop();
        st[u] = false;

        cur[u] = h[u];
        for (int i = h[u]; ~i; i = nex[i])
        {
            int j = num[i];
            if (dis[j] > dis[u] + cost[i] && f[i])
            {
                if (!st[j]) que.push(j), st[j] = true;
                dis[j] = dis[u] + cost[i];
            }
        }
    }
    return dis[T] != INF;
}

int dfs(int u, int a, int ed, int& res)
{
    if (u == ed || !a) return a;

    st[u] = true;//注意标记
    int flow = 0, F;
    for (int& i = cur[u]; ~i; i = nex[i])
    {
        int j = num[i];
        if (st[j]) continue;
        if (dis[j] == dis[u] + cost[i] && (F = dfs(j, min(a, f[i]), ed, res)))
        {
            res += F * cost[i];
            flow += F;
            a -= F;
            f[i] -= F;
            f[i ^ 1] += F;
            if (!a) break;
        }
    }
    st[u] = false;
    return flow;
}

inline int Maxflow(int S, int T, int& res) //res为费用
{
    int flow = 0, F;
    while (spfa(S, T))
        while (F = dfs(S, INF, T, res)) flow += F;
    return flow;
}
View Code

   另一种实现方法,比上面一个快

inline void add(int a, int b, int flow, int cos)
{
    num[dqx] = b;
    cost[dqx] = cos;
    Flow[dqx] = flow;
    nex[dqx] = h[a];
    h[a] = dqx++;

    num[dqx] = a;
    cost[dqx] = -cos;
    Flow[dqx] = 0;
    nex[dqx] = h[b];
    h[b] = dqx++;
}

inline bool spfa(int sta, int ed)
{
    que.push(sta);
    memset(dis, 0x3f, sizeof(dis));
    dis[sta] = 0;
    st[sta] = true;

    RE int u, i, j;
    while (!que.empty())
    {
        u = que.front();
        que.pop();

        st[u] = false;
        for (i = h[u]; ~i; i = nex[i])
        {
            j = num[i];
            if (dis[j] > dis[u] + cost[i] && Flow[i])
            {
                dis[j] = dis[u] + cost[i];
                sur[j] = i;
                if (!st[j])
                    que.push(j);
            }
        }
    }
    return dis[ed] != INF;
}

inline int Maxflow(int sta, int ed, int &res)
{
    int flow = 0;
    for (int f = INF, w; spfa(sta, ed); f = INF)
    {
        for (w = ed; w != sta; w = num[sur[w] ^ 1])
            f = min(f, Flow[sur[w]]);
        for (w = ed; w != sta; w = num[sur[w] ^ 1])
            Flow[sur[w]] -= f, Flow[sur[w] ^ 1] += f;
        res += dis[ed] * f;
        flow += f;
    }
    return flow;
}
View Code

 

posted on 2021-03-23 20:38  ArrogHie  阅读(107)  评论(0编辑  收藏  举报