上下界網絡流詳解


知乎算法學習筆記

上下界網絡流詳解

一、無源匯上下界可行流

無源匯上下界網絡流,如果有可行流,則必定是一副強連通分量。

模型

給定一個$n$個點$m$條邊的圖,每條邊有一個下限流量$L_{i,j}$和一個上限流量$R_{i,j}$,求出是否存在一種方案使得在滿足流量平衡的情況下所有邊均滿足上下界條件。

流量平衡:每個點流入的流量等於該點流出的流量

解決方法

首先每條邊的下限肯定是要流滿的,我們先讓每條邊流$L_{i,j}$那么多的流量,然后將其流量設為$R_{i,j}-L_{i,j}$,如圖所示:

觀察到只流下限時會出現流量不平衡的點,那么就會出現一些問題,如圖所示:

這樣一張圖明顯不存在可行流,但是如果我們減去$L_{i,j}$,就會得到:

如果不看約束條件的話,這張圖存在着可行流,且最大流為$10$。

這些問題都是由於流量不平衡導致的,那么我們怎么使流量平衡呢?

因為在一般的網絡流中,一個點的流入的流量都來自源點,流出的流量都會到匯點,所以我們可以新建超級源點和超級匯點,對於一個點,有如下三種情況:

$1.in_x>out_x:$多流入了流量應該由源點補足,連邊$(s,x,in_x-out_x)$

$2.in_x=out_x:$流量平衡,不需要連邊(當然如果想的話可以連)

$3.in_x<out_x:$多流出的流量應流入匯點,連邊$(x,t,out_x-in_x)$

所以前面的圖應變為:

以右上角的點為例,流入它的下界為25,流出的為10,為了使這兩條邊下界都為0,則兩條邊同時流過10的流量(流出的邊的下界),因此,兩條邊的上下界都-10。
變成了流入的邊[15, 45],流出的邊[0,20],然而,流入邊的下界依然沒有達到0,因此可以加一條從源點到該點流量為15的邊,而流入的邊,上界即為30,下界為0.

然后跑最大流算法,如果從$s$流出的邊均滿流,那么代表所有點的流量均平衡,所得到的結果就是原圖的一條可行流。

觀察可得,上圖無可行流。

二、有源匯上下界可行流

在原圖的基礎上多了源點與匯點,而這兩個點肯定流量不平衡,觀察到源點的流入一定等於匯點的流出,為了使兩個點流量平衡,我們增加邊$(t,s)$,下界為0,上界為$inf$,然后就可以使用無源匯上下界可行流的方法了。

[問題]之前考慮,為什么不可以使用本身的源點進行連邊,去平衡其他點的流量守恆。因為從S點流出的流量不確定,沒有辦法去計算新邊的流量是否滿流。

三、有源匯上下界最大流

先跑出一條可行流

觀察到上圖的可行流為$95$,我們接下來看殘量網絡:

觀察到超級源點與超級匯點連出的邊已經流滿(可行流存在的條件),而最大流等於可行流$+$還能向上浮動的流量,相當於盡量跑滿上限,

由於原圖中的源匯之間還可以繼續找增廣路,故還能向上浮動的流量就為$S$到$T$的最大流。

四、有源匯上下界最小流

跟上面幾乎完全相同,只需要在拆掉附加邊后,從匯點到源點,而不是從源點到匯點跑一遍最大流。可行流的流量,減去從匯點到源點的最大流即為答案。如果說上下界最大流是把殘量網絡“榨干”,那么上下界最小流就是把不需要的流“退回”。

注意:求出可行流后應刪去邊$(T,S,inf)$

 

無源匯上下界可行流


#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 210, M = (10200 + N) * 2, INF = 1e8;

int n, m, S, T;
int h[N], e[M], f[M], l[M], ne[M], idx;
int q[N], d[N], cur[N], A[N];

void add(int a, int b, int c, int d)
{
    e[idx] = b, f[idx] = d - c, l[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
    e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}

bool bfs()
{
    int hh = 0, tt = 0;
    memset(d, -1, sizeof d);
    q[0] = S, d[S] = 0, cur[S] = h[S];
    while (hh <= tt)
    {
        int t = q[hh ++ ];
        for (int i = h[t]; ~i; i = ne[i])
        {
            int ver = e[i];
            if (d[ver] == -1 && f[i])
            {
                d[ver] = d[t] + 1;
                cur[ver] = h[ver];
                if (ver == T) return true;
                q[ ++ tt] = ver;
            }
        }
    }
    return false;
}

int find(int u, int limit)
{
    if (u == T) return limit;
    int flow = 0;
    for (int i = cur[u]; ~i && flow < limit; i = ne[i])
    {
        cur[u] = i;
        int ver = e[i];
        if (d[ver] == d[u] + 1 && f[i])
        {
            int t = find(ver, min(f[i], limit - flow));
            if (!t) d[ver] = -1;
            f[i] -= t, f[i ^ 1] += t, flow += t;
        }
    }
    return flow;
}

int dinic()
{
    int r = 0, flow;
    while (bfs()) while (flow = find(S, INF)) r += flow;
    return r;
}

int main()
{
    scanf("%d%d", &n, &m);
    S = 0, T = n + 1;
    memset(h, -1, sizeof h);
    for (int i = 0; i < m; i ++ )
    {
        int a, b, c, d;
        scanf("%d%d%d%d", &a, &b, &c, &d);
        add(a, b, c, d);
        A[a] -= c, A[b] += c;
    }

    int tot = 0;
    for (int i = 1; i <= n; i ++ )
        if (A[i] > 0) add(S, i, 0, A[i]), tot += A[i];
        else if (A[i] < 0) add(i, T, 0, -A[i]);

    if (dinic() != tot) puts("NO");
    else
    {
        puts("YES");
        for (int i = 0; i < m * 2; i += 2)
            printf("%d\n", f[i ^ 1] + l[i]);
    }
    return 0;
}

作者:yxc

有源匯上下界最大流


#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 210, M = (N + 10000) * 2, INF = 1e8;

int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N], A[N];

void add(int a, int b, int c)
{
    e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
    e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}

bool bfs()
{
    int hh = 0, tt = 0;
    memset(d, -1, sizeof d);
    q[0] = S, d[S] = 0, cur[S] = h[S];
    while (hh <= tt)
    {
        int t = q[hh ++ ];
        for (int i = h[t]; ~i; i = ne[i])
        {
            int ver = e[i];
            if (d[ver] == -1 && f[i])
            {
                d[ver] = d[t] + 1;
                cur[ver] = h[ver];
                if (ver == T) return true;
                q[ ++ tt] = ver;
            }
        }
    }
    return false;
}

int find(int u, int limit)
{
    if (u == T) return limit;
    int flow = 0;
    for (int i = cur[u]; ~i && flow < limit; i = ne[i])
    {
        cur[u] = i;
        int ver = e[i];
        if (d[ver] == d[u] + 1 && f[i])
        {
            int t = find(ver, min(f[i], limit - flow));
            if (!t) d[ver] = -1;
            f[i] -= t, f[i ^ 1] += t, flow += t;
        }
    }
    return flow;
}

int dinic()
{
    int r = 0, flow;
    while (bfs()) while (flow = find(S, INF)) r += flow;
    return r;
}

int main()
{
    int s, t;
    scanf("%d%d%d%d", &n, &m, &s, &t);
    S = 0, T = n + 1;
    memset(h, -1, sizeof h);
    while (m -- )
    {
        int a, b, c, d;
        scanf("%d%d%d%d", &a, &b, &c, &d);
        add(a, b, d - c);
        A[a] -= c, A[b] += c;
    }

    int tot = 0;
    for (int i = 1; i <= n; i ++ )
        if (A[i] > 0) add(S, i, A[i]), tot += A[i];
        else if (A[i] < 0) add(i, T, -A[i]);

    add(t, s, INF);
    if (dinic() < tot) puts("No Solution");
    else
    {
        int res = f[idx - 1];
        S = s, T = t;
        f[idx - 1] = f[idx - 2] = 0;
        printf("%d\n", res + dinic());
    }

    return 0;
}

作者:yxc

有源匯上下界最小流


#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 50010, M = (N + 125003) * 2, INF = 2147483647;

int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N], A[N];

void add(int a, int b, int c)
{
    e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
    e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}

bool bfs()
{
    int hh = 0, tt = 0;
    memset(d, -1, sizeof d);
    q[0] = S, d[S] = 0, cur[S] = h[S];
    while (hh <= tt)
    {
        int t = q[hh ++ ];
        for (int i = h[t]; ~i; i = ne[i])
        {
            int ver = e[i];
            if (d[ver] == -1 && f[i])
            {
                d[ver] = d[t] + 1;
                cur[ver] = h[ver];
                if (ver == T) return true;
                q[ ++ tt] = ver;
            }
        }
    }
    return false;
}

int find(int u, int limit)
{
    if (u == T) return limit;
    int flow = 0;
    for (int i = cur[u]; ~i && flow < limit; i = ne[i])
    {
        cur[u] = i;
        int ver = e[i];
        if (d[ver] == d[u] + 1 && f[i])
        {
            int t = find(ver, min(f[i], limit - flow));
            if (!t) d[ver] = -1;
            f[i] -= t, f[i ^ 1] += t, flow += t;
        }
    }
    return flow;
}

int dinic()
{
    int r = 0, flow;
    while (bfs()) while (flow = find(S, INF)) r += flow;
    return r;
}

int main()
{
    int s, t;
    scanf("%d%d%d%d", &n, &m, &s, &t);
    S = 0, T = n + 1;
    memset(h, -1, sizeof h);
    while (m -- )
    {
        int a, b, c, d;
        scanf("%d%d%d%d", &a, &b, &c, &d);
        add(a, b, d - c);
        A[a] -= c, A[b] += c;
    }

    int tot = 0;
    for (int i = 1; i <= n; i ++ )
        if (A[i] > 0) add(S, i, A[i]), tot += A[i];
        else if (A[i] < 0) add(i, T, -A[i]);

    add(t, s, INF);

    if (dinic() < tot) puts("No Solution");
    else
    {
        int res = f[idx - 1];
        S = t, T = s;
        f[idx - 1] = f[idx - 2] = 0;
        printf("%d\n", res - dinic());
    }

    return 0;
}

作者:yxc

網絡流之關鍵邊

https://www.acwing.com/problem/content/2238/

#include<iotream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<quque>
#define inf 0x7fffffff
using namespace std;
const int mxm = 10010;
int cnt = 1, res, b[505], S, T, f[mxm];
struct{
    int to,nxt,w;
}edge[mxm];
void add(int u, int v, int w){
    cnt++;
    edge[cnt].to = v;
    edge[cnt].nxt = hd[u];
    edge[cnt].w = w;
    hd[u] = cnt;
}
bool bfs(){
    queue<int> q; memset(d, -1, sizeof d);
    q.push(S);
    while(q.empty()){
        int u = q.front(); q.pop();
        for(int i = hd[u]; ~i; i = edge[i].nxt){
            int v = edge[i].to;
            if(d[v] == -1 && edge[i].w){
                d[v] = d[u] + 1;
                cur[v] = hd[v];
                if(v == T) return 1;
                q.push(v);
            }
        }
    }
    return 0;
}
int find(int u, int res){
    if(u == T) return res;
    int flow = 0;
    for(int &i = hd[u]; ~i; i = edge[i].nxt){
        int v = edge[i].to;
        if(d[v] == d[u] + 1 && f[i]){
            int t = find(v, min(edge[i].w, res - flow);
            if(!t) d[v] = -1;
            edge[i].w -= t, edge[i^1].w += t, flow += t;
        }
    }
    return flow;
}
void dinic(){
    int mxflow = 0, flow;
    while(bfs()){
        flow = 0;
        while(flow = dinic(S, inf)) mxflow += flow;
    }
    return mxflow;
}
void dfs(int u, int vis[], int fl){
    vis[u] = 1;
    for(int i = hd[u]; ~i; i = edge[i].nxt){
        int j = i ^ fl, v = edge[i].to;
        if(f[j] && !vis[v])
            dfs(v, vis, fl);
    }
}
int main(){
    scanf("%d%d",&n,&m);
    S = 0, T = n-1;
    memset(h, -1, sizeof h);
    for(int i = 0; i < m; i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(a, b, c);
        add(b, a, 0);
    }
    dinic();
    dfs(S, vis_s, 0);
    dfs(T, vis_t, 1);
    int res;
    for(int i = 0; i < 2*m; i += 2){
        if(!f[i] && vis_s[edge[i^1].to] && vis_t[edge[i].to]){//反向邊的to就是正向邊的起點
            res++;
        }
    }
    printf("%d\n",res);
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM