网络流算法-最大流问题总结-(Edmonds_Karp+Dinic优化+ISAP优化)

EK算法的思路:

1.通过BFS扩展合法节点,找到汇点,并记录pre前一个节点,如果找不到增广路,算法结束

2.通过BFS的记录,从汇点回溯到起点,记录下每条边的 res残流

3.将所有经过的边减去res 反向边加上

4.重复上面的步骤,直到找不到增广路,结束

1.朴素EK: 

通过邻接矩阵来储存

但是复杂度太大 N^2

附上源代码 :

const int MAX=220;
int pre[MAX];
int flow[MAX][MAX];
int cap[MAX][MAX];
int res[MAX];//res残量
int Edmonds_Karp(int s,int e){ //EK算法
    int maxflow=0;
    mms(flow,0);
    mms(pre,0);
    queue<int>q;
    while (true) {
        mms(res,0);
        res[s]=INF;
        q.push(s);
        while (!q.empty()) {
            int u=q.front();
            q.pop();
            for(int v=1;v<=e;v++){
                if(!res[v]&&cap[u][v]>flow[u][v]){
                    res[v]=min(res[u],cap[u][v]-flow[u][v]);
                    pre[v]=u;
                    q.push(v);
                }
            }
        }
        while(res[e]==0) return maxflow;
        for(int u=e;u!=s;u=pre[u]){
            flow[pre[u]][u]+=res[e];
            flow[u][pre[u]]-=res[e];
        }
        maxflow+=res[e];
    }
}
int main(){
    
    
    int t;
    cin>>t;
    while (t--) {
        //
        mms(cap,0);
        int n,m;
        cin>>n>>m;
        //for
        for(int i=0;i<m;i++){
            int u,v,w;
            cin>>u>>v>>w;
            cap[u][v]+=w;
        }
//        cout<<Edmonds_Karp(1,n)<<endl;
        int k=1;
        cout<<"Case "<<k++<<": "<<Edmonds_Karp(1,n)<<endl;
    }
    
//    int u,v,w;
//    cap[u][v]+=w;//+=考虑重边的问题
    //
}

 2.无优化的Dinic算法:

const int MAX=10010;
int n,m,s,t;
struct E{
    int next,to,w; //对应边的残量
}e[MAX];
int head[MAX];
int cnt=0;  //反边直接^
void add(int u,int v,int w){ //前向星存图
    e[cnt].to=v;
    e[cnt].w=w;
    e[cnt].next=head[u];
    head[u]=cnt++;
}
int dep[MAX]; //深度
queue<int >q;
int bfs(){ //bfs分层图
    while (!q.empty()) { //先清空
        q.pop();
    }
    mms(dep,0);
    dep[s]=1;
    q.push(s);
    while (!q.empty()) {
        int u=q.front();
        q.pop();
        for(re int i=head[u];i!=-1;i=e[i].next){
            int v=e[i].to;
            if(e[i].w>0&&dep[v]==0){
                dep[v]=dep[u]+1;
                q.push(v);
            }
            
        }
        
    }
    if(dep[t]==0) return 0; //当汇点的深度不存在时候 说明不存在分层图 同时也说明了不存在增广路
    return 1;
}
int dfs(int u,int dist){ //u是当前的节点 dist是当前的流量
    if(u==t)return dist;
    for(re int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(dep[v]==dep[u]+1&&e[i].w!=0){
            int d=dfs(v,min(dist,e[i].w));
            if(d>0){ //增广成功
                e[i].w-=d; //正边-
                e[i^1].w+=d;//反向边+
                return d; //向上传递
            }
        }
    }
    return 0;
}
int Dinic(){
    int ans=0;
    while (bfs()) {
        ans+=dfs(s,INF);
    }
    return ans;
}
int main(){
    mms(head,-1);
    n=read();
    m=read();
    s=read();
    t=read();
    for(re int i=1,x,y,z;i<=m;i++){
        x=read();
        y=read();
        z=read();
        add(x,y,z);
        add(y,x,0);
    }
    cout<<Dinic()<<endl;
    return 0;
}

3.Dinic+当前弧优化:

const int MAX=10010;
int n,m,s,t;
struct E{
    int next,to,w; //对应边的残量
}e[MAX];
int head[MAX];
int cnt=0;  //反边直接^
void add(int u,int v,int w){ //前向星存图
    e[cnt].to=v;
    e[cnt].w=w;
    e[cnt].next=head[u];
    head[u]=cnt++;
}

//当前弧优化部分
int rec[MAX];  //记录u循环到了那条边
//end

int dep[MAX]; //深度
queue<int >q;
int bfs(){ //bfs分层图
    while (!q.empty()) { //先清空
        q.pop();
    }
    mms(dep,0);
    dep[s]=1;
    q.push(s);
    while (!q.empty()) {
        int u=q.front();
        q.pop();
        for(re int i=head[u];i!=-1;i=e[i].next){
            int v=e[i].to;
            if(e[i].w>0&&dep[v]==0){
                dep[v]=dep[u]+1;
                q.push(v);
            }
            
        }
        
    }
    if(dep[t]==0) return 0; //当汇点的深度不存在时候 说明不存在分层图 同时也说明了不存在增广路
    return 1;
}
int dfs(int u,int dist){ //u是当前的节点 dist是当前的流量
    if(u==t)return dist;
    //优化部分当前弧
    for(re int &i=rec[u];i!=-1;i=e[i].next){ //&可以起到增加i的同时增加cur[u]的值
        int v=e[i].to;
        if(dep[v]==dep[u]+1&&e[i].w!=0){
            int d=dfs(v,min(dist,e[i].w));
            if(d>0){ //增广成功
                e[i].w-=d; //正边-
                e[i^1].w+=d;//反向边+
                return d; //向上传递
            }
        }
    }
    return 0;
    
    
    
    //未优化
//    for(re int i=head[u];i!=-1;i=e[i].next){
//        int v=e[i].to;
//        if(dep[v]==dep[u]+1&&e[i].w!=0){
//            int d=dfs(v,min(dist,e[i].w));
//            if(d>0){ //增广成功
//                e[i].w-=d; //正边-
//                e[i^1].w+=d;//反向边+
//                return d; //向上传递
//            }
//        }
//    }
//    return 0;
}
int Dinic(){
    int ans=0;
    //未优化
//    while (bfs()) {
//        ans+=dfs(s,INF);
//    }
    //当前弧优化
    while (bfs()) {
        for(re int i=1;i<=n;i++){
            rec[i]=head[i]; //每次建完图以后都要把rec重置到每个点的第一条边
        }
        ans+=dfs(s,INF);
    }
    return ans;
}
int main(){
    mms(head,-1);
    n=read();
    m=read();
    s=read();
    t=read();
    for(re int i=1,x,y,z;i<=m;i++){
        x=read();
        y=read();
        z=read();
        add(x,y,z);
        add(y,x,0);
    }
    cout<<Dinic()<<endl;
    return 0;
}

4.Dinic 多路增广优化+当前弧优化:

const int MAX=10010;
int n,m,s,t;
struct E{
    int next,to,w; //对应边的残量
}e[MAX];
int head[MAX];
int cnt=0;  //反边直接^
void add(int u,int v,int w){ //前向星存图
    e[cnt].to=v;
    e[cnt].w=w;
    e[cnt].next=head[u];
    head[u]=cnt++;
}

//当前弧优化部分
int rec[MAX];  //记录u循环到了那条边
//end
int  maxflow;
int dep[MAX]; //深度
queue<int >q;
int bfs(){ //bfs分层图
    while (!q.empty()) { //先清空
        q.pop();
    }
    mms(dep,0);
    dep[s]=1;
    q.push(s);
    while (!q.empty()) {
        int u=q.front();
        q.pop();
        for(re int i=head[u];i!=-1;i=e[i].next){
            int v=e[i].to;
            if(e[i].w>0&&dep[v]==0){
                dep[v]=dep[u]+1;
                q.push(v);
            }
            
        }
        
    }
    if(dep[t]==0) return 0; //当汇点的深度不存在时候 说明不存在分层图 同时也说明了不存在增广路
    return 1;
}
//当前弧优化
//int dfs(int u,int dist){ //u是当前的节点 dist是当前的流量
//    if(u==t)return dist;
//    //优化部分当前弧
//    for(re int &i=rec[u];i!=-1;i=e[i].next){ //&可以起到增加i的同时增加cur[u]的值
//        int v=e[i].to;
//        if(dep[v]==dep[u]+1&&e[i].w!=0){
//            int d=dfs(v,min(dist,e[i].w));
//            if(d>0){ //增广成功
//                e[i].w-=d; //正边-
//                e[i^1].w+=d;//反向边+
//                return d; //向上传递
//            }
//        }
//    }
//    return 0;
//
//
//
//    //未优化
    for(re int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(dep[v]==dep[u]+1&&e[i].w!=0){
            int d=dfs(v,min(dist,e[i].w));
            if(d>0){ //增广成功
                e[i].w-=d; //正边-
                e[i^1].w+=d;//反向边+
                return d; //向上传递
            }
        }
    }
    return 0;
//}
//多路增广优化
int dfs(int u,int flow){
    if(u==t){
        maxflow+=flow;
        return flow;
    }
    int used=0; //表示这个点的流量用了多少
    for(re int &i=rec[u];~i;i=e[i].next){
        int v=e[i].to;
        if(dep[v]==dep[u]+1&&e[i].w!=0){
            int d=dfs(v,min(flow-used,e[i].w));
            if(d>0){
                e[i].w-=d;
                e[i^1].w+=d;
                used+=d;
                if(used==flow) break; //这点的流量满了 不需要找了
                //dfs找到一条增广路 不需要返回 只需要修改used就行
                //如果used没有到达flow的上限 就可以继续寻找
            }
        }
    }
    return used;
}
int Dinic(){
    int ans=0;
    //未优化
//    while (bfs()) {
//        ans+=dfs(s,INF);
//    }
    //当前弧优化
    while (bfs()) {
        for(re int i=1;i<=n;i++){
            rec[i]=head[i]; //每次建完图以后都要把rec重置到每个点的第一条边
        }
        ans+=dfs(s,INF);
    }
    return ans;
}
int main(){
    mms(head,-1);
    n=read();
    m=read();
    s=read();
    t=read();
    for(re int i=1,x,y,z;i<=m;i++){
        x=read();
        y=read();
        z=read();
        add(x,y,z);
        add(y,x,0);
    }
    cout<<Dinic()<<endl;
    return 0;
}

5.ISAP+gap优化+多路增广:

const int MAX=10010;
int n,m,s,t;
struct E{
    int next,to,w; //对应边的残量
}e[MAX<<1]; //双向边*2
int head[MAX];
int gap[MAX]; //gap是深度为i的点的数量 优化
int cnt=0;  //反边直接^
void add(int u,int v,int w){ //前向星存图
    e[cnt].to=v;
    e[cnt].w=w;
    e[cnt].next=head[u];
    head[u]=cnt++;
}

//当前弧优化部分
int rec[MAX];  //记录u循环到了那条边
//end
int  maxflow;
int dep[MAX]; //深度
queue<int >q;
int bfs(){ //bfs分层图
    mms(dep,-1);
    mms(gap,0);
    q.push(t);
    dep[t]=0;
    gap[0]=1;
    while (!q.empty()) {
        int u=q.front();
        q.pop();
        for(re int i=head[u];i!=-1;i=e[i].next){
            int v=e[i].to;
            if(dep[v]!=-1) continue; //防止重复修改某个点
            q.push(v);
            dep[v]=dep[u]+1;
            gap[dep[v]]++;
        }
        
    }
    if(dep[t]==0) return 0; //当汇点的深度不存在时候 说明不存在分层图 同时也说明了不存在增广路
    return 1;
}
int dfs(int u,int flow){
    if(u==t) {
        maxflow+=flow;
        return flow;
    }
    int used=0;
    for(re int i=head[u];~i;i=e[i].next){
        int v=e[i].to;
        if(e[i].w&&dep[v]+1==dep[u]){
            int d=dfs(v,min(flow-used,e[i].w));
            if(d>0){
                e[i].w-=d;
                e[i^1].w+=d;
                used+=d;
            }
            if(used==flow){
                return used;
            }
        }
    }
    --gap[dep[u]];
    if(!gap[dep[u]]) dep[s]=n+1;
    dep[u]++;
    gap[dep[u]]++;
    return used;
}
void ISAP(int s,int t){
    bfs();//从t到s跑一遍bfs 标记初始深度
    while (dep[s]<n) {
        dfs(s,INF);
    }
    //终止条件 dep[s]<n
    //每走一次增广路 s的层数+1 若一直没有断层 最多跑n-dep(刚bfs完s的深度)
}
int main(){
    mms(head,-1);
    n=read();
    m=read();
    s=read();
    t=read();
    for(re int i=1,x,y,z;i<=m;i++){
        x=read();
        y=read();
        z=read();
        add(x,y,z);
        add(y,x,0);
    }
    ISAP(s,t);
    cout<<maxflow<<endl;
}

猜你喜欢

转载自blog.csdn.net/m0_57006708/article/details/120664589