Network flow-maximum flow (Ford-Fulkerson algorithm & Dinic algorithm)

Maximum flow

Just like water flow, there is a picture with both a starting point and an ending point. The maximum flow from the starting point to the end is the maximum flow.
Insert picture description here
In the above figure, assuming that 1 is the starting point and 4 is the end point, then the maximum flow is 3.
That is, the sum of the flow of the two flows 1 -> 3 -> 4 and 1 -> 2 -> 4, and it can be known that the flow of each road depends on the short board of the road, that is, the smallest side.

FF algorithm:

Zengguang Road:

If when calculating the maximum flow, a road has been run (for example, 1 -> 3 -> 4, the flow of this road is 2), then the flow of this road should be subtracted from all sides of this road. This is obviously easy to see, because it is to subtract the occupied flow of each side, and usually the remaining capacity after one side is deducted becomes the residual flow.

Then the Augmented Road is a road where all sides from the start point to the end point are not zero.

Reverse side:

The FF algorithm actually finds the Zengguang road every dfs and subtracts the flow of this road from all sides of the road. The sum of the flow of all roads is the maximum flow.
But here comes the problem. Sometimes there may be wrong augmentation paths. In other words, the augmentation path of dfs may not be optimal every time.
Insert picture description here
For example, in the picture above, if you start with 1 -> 2 -> 3 -> 4, then there will be no augmentation. Because the edge 3 -> 4 is used, the flow of the edge 1 -> 3 cannot flow to the end. Then you need to establish a negative side at this time. That is to create an edge from 3 -> 2.

When taking the path of augmentation, we subtract the capacity of the forward side and add the corresponding capacity to the reverse side.
Then after running 1 -> 2 -> 3 ->4, the capacity of 2 -> 3 is 1, and the capacity of 3 -> 2 (that is, the opposite side) is 2, then the next time the shortest path is run from 1- After> 3, you can go on the opposite road 3 -> 2. Then go 2 -> 4. So in the end, the correct maximum flow is still obtained.

Regarding the most negative side, I personally have the following doubts

  1. Will there be unlimited dfs augmentation roads when the reverse side is added?
  2. With the addition of the negative side, will the actual value of the maximum flow be affected?

After thinking about it, I feel that I can explain it like this:

It is impossible for 1, because the flow can only flow from the start point to the end point. When the capacity of the edge connected by the start point is exhausted, the flow cannot flow out anyway, so after adding the opposite edge, the augmentation road cannot be infinite of.

For 2 I think so, since it was originally not optimal, then it means that the optimal road is occupied by a certain side, then the occupied road must be not the optimal road, that is, it vacates a road , Then you can use the opposite side to fill the vacant road. In fact, A takes the road of B, and then gives B a chance to take the road of A. Because the final flow is the sum of the two roads A and B , So the final flow rate has not changed.

The representation of the negative side is very simple, use an even number to represent the positive side, and the adjacent odd number to represent the corresponding negative side. When the positive side finds the negative side or the negative side finds the positive side, only 1 needs to be XORed.

Code:
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 1e5 + 5;
int n,m,cnt = 2,h[maxn];
int st,ed;
bool vis[maxn];
struct node{
    
    
    int w,e,nex;
}e[maxn * 2];
void add(int a,int b,int c){
    
    
    e[cnt].e = b;
    e[cnt].nex = h[a];
    e[cnt].w = c;
    h[a] = cnt++;
}
int dfs(int x,int flow){
    
    //参数为当前节点与当前该增光路的最大流
    vis[x] = 1;
    if(ed == x)return flow;//如果到达终点就返回
    for(int i = h[x]; i ; i = e[i].nex){
    
    
        int endd = e[i].e,w = e[i].w;
        if(!vis[endd] && w){
    
    //不被访问而且该路仍有流就可以走
            int g = dfs(endd, min(flow,w));//走一遍该路,当前流最大值取决于短板
            if(g){
    
    
                e[i].w -= g;//当前流减少g
                e[i ^ 1].w += g;//对应的反边增加g
                return g;
            }
        }
    }
    return 0;
}

int main(){
    
    
    int a,b,c;
    scanf("%d %d %d %d",&n,&m,&st,&ed);
    for(int i = 1; i <= m; i++){
    
    
        scanf("%d %d %d",&a,&b,&c);
        add(a,b,c);//正边
        add(b,a,0);//反边
    }
    int ans = 0;
    while(true){
    
    
        memset(vis,0,sizeof vis);
        int g = dfs(st,INF);
        if(!g)break;//如果流为空就退出
        else ans += g;
    }
    printf("%d",ans);
    return 0;
}
/*
4 5 1 4
1 2 4
1 3 3
2 3 2
2 4 2
3 4 6
*/

Dinic algorithm:

The Dinic algorithm uses a hierarchical graph to optimize the FF algorithm.
For each node, it is artificially stipulated that it can only flow to the next layer, and if it does not flow, it will end dfs.

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 1e5 + 5;
int n,m,cnt = 2,h[maxn];
int st,ed,dep[maxn];//dep用来存节点的深度
struct node{
    
    
    int w,e,nex;
}e[maxn * 2];
void add(int a,int b,int c){
    
    
    e[cnt].e = b;
    e[cnt].nex = h[a];
    e[cnt].w = c;
    h[a] = cnt++;
}
bool bfs(){
    
    //用于判断当前道路是否流通
    memset(dep,0,sizeof dep);//清空数组
    dep[st] = 1;
    queue<int> qu;
    qu.push(st);
    while(!qu.empty()){
    
    
        int t = qu.front();
        qu.pop();
        for(int i = h[t]; i ; i = e[i].nex){
    
    
            int endd = e[i].e,w = e[i].w;
            if(w && !dep[endd]){
    
    //没被标记,以及仍然有残余流量就继续走
                dep[endd] = dep[t] + 1;
                qu.push(endd);
            }
        }
    }
    if(!dep[ed])return false;//到不达就返回否
    else return true;
}
int dfs(int x,int flow){
    
    //参数为当前节点与当前流向该节点的流
    if(ed == x)return flow;//如果到达终点就返回
    int sum = 0;//表示当前的最大流
    for(int i = h[x]; i ; i = e[i].nex){
    
    
        int endd = e[i].e,w = e[i].w;
        if(dep[x] + 1 == dep[endd] && w){
    
    // 只走下一层
            int g = dfs(endd, min(flow,w));//走一遍该路,当前流最大值取决于短板
            flow -= g,sum += g;//到达x的流量减少g,当前的最大流增加g
            e[i].w -= g;//当前流减少g
            e[i ^ 1].w += g;//对应的反边增加g
        }
        if(!flow)break;//如果当前已经没有流到x的流了就退出
    }
    if(!sum)dep[x] = 0;//如果当前的最大流为0那么说明此节点之后的层不可能流向终点,那么就不要让其他节点的流再经过这个节点了
    return sum;//返回当前最大流
}

int main(){
    
    
    int a,b,c;
    scanf("%d %d %d %d",&n,&m,&st,&ed);
    for(int i = 1; i <= m; i++){
    
    
        scanf("%d %d %d",&a,&b,&c);
        add(a,b,c);//正边
        add(b,a,0);//反边
    }
    int ans = 0;
    while(bfs())ans += dfs(st,INF);
    printf("%d",ans);
    return 0;
}

Dinic + current arc optimization

In fact, if you say that the layer behind a point has no traffic contribution, then you can skip this point, so you only need to add a cur array to record the remaining edges.

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 1e5 + 5;
int n,m,cnt = 2,h[maxn];
int st,ed,dep[maxn],cur[maxn];//dep用来存节点的深度
struct node{
    
    
    int w,e,nex;
}e[maxn * 2];
void add(int a,int b,int c){
    
    
    e[cnt].e = b;
    e[cnt].nex = h[a];
    e[cnt].w = c;
    h[a] = cnt++;
}
bool bfs(){
    
    //用于判断当前道路是否流通
    memset(dep,0,sizeof dep);//清空数组
    memcpy(cur,h,sizeof h);//每次都要复制一下h
    dep[st] = 1;
    queue<int> qu;
    qu.push(st);
    while(!qu.empty()){
    
    
        int t = qu.front();
        qu.pop();
        for(int i = h[t]; i ; i = e[i].nex){
    
    
            int endd = e[i].e,w = e[i].w;
            if(w && !dep[endd]){
    
    
                dep[endd] = dep[t] + 1;
                qu.push(endd);
            }
        }
    }
    if(!dep[ed])return false;
    else return true;
}
int dfs(int x,int flow){
    
    
    if(ed == x)return flow;
    int sum = 0;
    for(int i = cur[x]; i ; i = e[i].nex){
    
    
    	cur[x] = i;//记录剩下的边
        int endd = e[i].e,w = e[i].w;
        if(dep[x] + 1 == dep[endd] && w){
    
    
            int g = dfs(endd, min(flow,w));
            flow -= g,sum += g;
            e[i].w -= g;
            e[i ^ 1].w += g;
        }
        if(!flow)break;
    }
    if(!sum)dep[x] = 0;
    return sum;
}

int main(){
    
    
    int a,b,c;
    scanf("%d %d %d %d",&n,&m,&st,&ed);
    for(int i = 1; i <= m; i++){
    
    
        scanf("%d %d %d",&a,&b,&c);
        add(a,b,c);//正边
        add(b,a,0);//反边
    }
    int ans = 0;
    while(bfs())ans += dfs(st,INF);
    printf("%d",ans);
    return 0;
}

Luogu board question
pay attention to the scope, the board is fine, the question may burst int

Guess you like

Origin blog.csdn.net/qq_36102055/article/details/107326736