P3376 【模板】網絡最大流( Edmonds-krap、Dinic、ISAP 算法)

P3376 【模板】網絡最大流( Edmonds-krap、Dinic、ISAP 算法)

題目描述 如題,給出一個網絡圖,以及其源點和匯點,求出其網絡最大流。

輸入格式 第一行包含四個正整數N、M、S、T,分別表示點的個數、有向邊的個數、源點序號、匯點序號。

接下來M行每行包含三個正整數ui、vi、wi,表示第i條有向邊從ui出發,到達vi,邊權爲wi(即該邊最大流量爲wi)

輸出格式 一行,包含一個正整數,即爲該網絡的最大流。

輸入輸出樣例

輸入 #1 複製
4 5 4 3
4 2 30
4 3 20
2 3 20
2 1 30
1 3 40
輸出 #1 複製
50

說明/提示
時空限制:1000ms,128M

數據規模:

對於30%的數據:N<=10,M<=25

對於70%的數據:N<=200,M<=1000

對於100%的數據:N<=10000,M<=100000

樣例說明:

在這裏插入圖片描述

題目中存在3條路徑:

4–>2–>3,該路線可通過20的流量

4–>3,可通過20的流量

4–>2–>1–>3,可通過10的流量(邊4–>2之前已經耗費了20的流量)

故流量總計20+20+10=50。輸出50。

思路

最大流板子。。。。
大佬博客傳送門1
傳送門2
傳送門3
傳說門4

題解一(Edmonds-krap)

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;

#define INF 0x3f3f3f3f
const int maxn = 10005;
const int maxm = 300005;
int n,m,s,e;

struct Edge
{
    int u,v,next;
    int cap,flow;
} edge[2*maxm];
int head[2*maxm];

int k = -1;
void Add(int u, int v, int w)
{
    edge[++ k] = (Edge){ u, v, head[u], w, 0};
    head[u] = k;
    edge[++ k] = (Edge){ v, u, head[v], 0, 0};
    head[v] = k;
}
int flag = 0;  //flag == 0 表示還能找到增廣路

int bfs(int s, int e)
{
    int min_flow[maxn];     //min_flow[i] 表示到達第 i 號節點時,之前所有的路徑中 最小的 邊的流量cap - 已經流過的流量flow
    int pre[maxm];          //記錄增光路時,當前節點是由那個那條邊找到的,這樣在找到增光路之後,我們可以根據 pre[] 數組回推其他 組成該增光路的邊
    memset(min_flow, 0, sizeof(min_flow));       //min_flow[i] == 0 表示之前沒有走過 i 節點
    min_flow[s] = INF;
    pre[s] = -1;
    queue<int> q;
    q.push(s);
    int u,v,flow;
    while(! q.empty())
    {
        u = q.front(); q.pop();

        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            v = edge[i].v;
            flow = edge[i].cap - edge[i].flow;
            if(flow > 0 && ! min_flow[v])   //所走的這條邊還有 剩餘的空間cap,並且該節點還沒有被走過。。
            {
                pre[v] = i;
                min_flow[v] = min(min_flow[u], flow);
                q.push(v);
            }
        }
        if(min_flow[e])
            break;
    }
    if(! min_flow[e])       //一隻到更新結束,終點到最小增加值還是等於0,那麼說從起點到終點已經沒有增廣路了
        flag = 1;
    //這一點千萬不要忘,,,,把用過的水流在這條增光路上都減去。。。。
    for(int j = e; pre[j] != -1;  j = edge[pre[j]].u)
    {
        edge[pre[j]].flow += min_flow[e];
        edge[pre[e]^1].flow -= min_flow[e];
    }
    return min_flow[e];     //返回最小增加值到 答案中
}

int max_flow()
{
    int mx_flw = 0;
    while(1)
    {
        mx_flw += bfs(s, e);
        if(flag)
            break;
    }
    return mx_flw;
}

void init()
{
    memset(head, -1, sizeof(head));
    k = -1;
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(nullptr);
    //freopen("T.txt","r",stdin);
    cin >> n >> m >> s >> e;
    init();
    int u,v,w;
    for(int i = 1; i <= m; i ++)
    {
        cin >> u >> v >> w;
        Add(u, v, w);
    }
    cout << max_flow() << endl;

    return 0;
}



題解二(Dinic)

#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;

#define INF 1e9
#define ll long long
const int maxn = 100005;
const int maxm = 150000;
int n,m;

struct Edge
{
    int v;
    ll w;
    int next;
} edge[maxm];
int head[maxn], cur[maxn];
int deep[maxn];

int k;
void Add(int u, int v, ll w)
{
    edge[++ k] = (Edge){ v, w, head[u]};
    head[u] = k;
    edge[++ k] = (Edge){ u, 0, head[v]};
    head[v] = k;
}
//bfs 爲圖分層

bool bfs(int s, int e)
{
    memset(deep, 0x7f, sizeof(deep));
    for(int i = 1; i <= n; i ++) cur[i] = head[i];
    deep[s] = 0;
    queue<int> q;
    q.push(s);
    int u,v;
    ll w;
    while(! q.empty())
    {
        u = q.front(); q.pop();

        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            v = edge[i].v;
            w = edge[i].w;
            if(w && deep[v] > INF)
            {
                deep[v] = deep[u] + 1;
                q.push(v);
            }
        }
    }
    if(deep[e] >= INF)
        return false;
    return true;
}

//dfs 一次找多條增光路(相當於找了一個增廣網。。)
ll dfs(int now, int e, ll limit)
{
    if(! limit || now == e) return limit;

    ll flow = 0, f;
    for(int i = cur[now]; i != -1; i = edge[i].next)
    {
        cur[now] = i;   //千萬注意這一步的意思啊。。這一步是因爲 dfs的特性是每次增廣一定是增廣,那麼下一次就不必再檢查它了,而直接看第一個未被檢查的邊
        if(deep[edge[i].v] == deep[now] + 1 && (f = dfs(edge[i].v, e, min(limit, edge[i].w))))
        {
            flow += f;
            limit -= f;
            edge[i].w -= f;
            edge[i^1].w += f;
            if(! limit)
                break;
        }
    }
    return flow;
}

ll Dinic(int s, int e)
{
    ll mx_flw = 0;
    while(bfs(s, e))
        mx_flw += dfs(s, e, INF);
    return mx_flw;
}


void init()
{
    k = -1;
    memset(head, -1, sizeof(head));
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(0);
    //freopen("T.txt","r",stdin);
    int s,e;
    cin >> n >> m >> s >> e;
    init();
    int u,v;
    ll w;
    for(int i = 1; i <= m; i ++)
        cin >> u >> v >> w, Add(u, v, w);
    cout << Dinic(s, e);

    return 0;
}



題解三(ISAP)

#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;

#define INF 1e9
const int maxn = 10005;
const int maxm = 200005;
int n, m, s, e;
int k;

struct Edge
{
    int v,w,next;
} edge[2 * maxm];

int head[maxn],cur[maxn],deep[maxn];
int last[maxm];
int num[maxm];      // num 桶,用來統計每個分層的節點的數量

void Add(int u, int v, int w)
{
    edge[++ k] = (Edge){ v, w, head[u]};
    head[u] = k;
    edge[++ k] = (Edge){ u, 0, head[v]};
    head[v] = k;
}

//bfs 用於更新deep層
void bfs(int e)
{
//    for(int i = 0; i <= n; i ++)
//        cur[i] = head[i], deep[i] = n;
    for(int i = 0; i <= m; i ++) cur[i] = head[i];
    for(int i = 1; i <= n; i ++) deep[i] = n;
    deep[e] = 0;
    queue<int> q;
    q.push(e);
    int u, v, w;
    while(! q.empty())
    {
        u = q.front(); q.pop();

        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            v = edge[i].v;
            if(edge[i^1].w && deep[v] == n)      //正圖 邊存在 且 v這個節點沒有被求過
            {
                deep[v] = deep[u] + 1;
                q.push(v);
            }
        }
    }
}

int Add_flow(int s, int e)
{
    int ans = INF;
    int now = e;
    while(now != s)
    {
        ans = min(ans, edge[last[now]].w);
        now = edge[last[now]^1].v;
    }
    now = e;
    while(now != s)
    {
        edge[last[now]].w -= ans;
        edge[last[now]^1].w += ans;
        now = edge[last[now]^1].v;
    }
    return ans;
}

int isap(int s, int e)
{
    int now = s;    //從起點開始進行操作
    bfs(e);         //先找出來一條邊 被操作的增光路
    for(int i = 1; i <= n; i ++) num[deep[i]] ++;
    int mx_flw = 0;
    while(deep[s] < n)
    {
        if(now == e)        //如果到達匯點直接增廣,重新回到源點進行下一輪增廣
        {
            mx_flw += Add_flow(s, e);
            now = s;
        }
        bool has_find = 0;
        for(int i = cur[now]; i != -1; i = edge[i].next)
        {
            if(edge[i].w && deep[now] == deep[edge[i].v] + 1)
            {
                has_find = 1;   //做標記已經找到一種可行路徑
                cur[now] = i;     //優化當前弧
                now = edge[i].v;
                last[edge[i].v] = i;
                break;
            }
        }

        if(! has_find)
        {
            int minn = n - 1;
            for(int i = head[now]; i != -1; i = edge[i].next)
                if(edge[i].w)
                    minn = min(minn, deep[edge[i].v]);
            if( (-- num[deep[now]]) == 0) break;   //gap 優化出現了斷層
            num[deep[now] = minn + 1] ++;
            cur[now] = head[now];
            if(now != s)
                now = edge[last[now]^1].v;
        }
    }
    return mx_flw;
}


void init()
{
    k = -1;
    memset(head, -1, sizeof(head));
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(0);
    //freopen("T.txt","r",stdin);
    cin >> n >> m >> s >> e;
    init();
    int u,v,w;
    for(int i = 1; i <= m; i ++)
        cin >> u >> v >> w, Add(u, v, w);
    cout << isap(s, e);
    return 0;
}


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