SAP算法

104 篇文章 0 订阅

https://www.cnblogs.com/wally/archive/2013/05/03/3054778.html

邻接矩阵

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;
#define N 202
#define INF 0x3f3f3f3f

int e[N][N];
int pre[N]; //记录当前点的前驱。
int d[N];   //记录距离标号  i-t距离的下界。
int num[N];  //gap优化,每个距离下标下的节点编号有多少个,为0的话,说明s-t不连通

int SAP(int s,int t){
    memset(pre,-1,sizeof(pre));
    memset(d,0,sizeof(d));
    memset(num,0,sizeof(num));
    num[0]=t;
    int v,u=pre[s]=s,flow=0,aug=INF;
    while(d[s]<t){  //else 残量网络中不存在s-t路。
        //寻找可行弧
        for(v=1;v<=t;v++){
            if(e[u][v]>0&&d[u]==d[v]+1){
                break;
            }
        }
        if(v<=t){
            pre[v]=u;
            u=v;
            if(v==t){
                aug=INF;
                //寻找当前找到路径上的最大流
                for(int i=v;i!=s;i=pre[i]){
                    if(aug>e[pre[i]][i]) aug=e[pre[i]][i];
                }
                flow+=aug;
                //更新残留网络。
                for(int i=v;i!=s;i=pre[i]){
                    e[pre[i]][i]-=aug;
                    e[i][pre[i]]+=aug;
                }
                u=s;        //从源点开始继续搜。
            }
        }else{
            //找不到可行弧
            int minlevel=t;
            //寻找与当前点连接的最小的距离标号。
            for(v=1;v<=t;v++){
                if(e[u][v]>0&&minlevel>d[v]){
                    minlevel=d[v];
                }
            }
            num[d[u]]--;            //当前标号的数目减一
            if(!num[d[u]]) break; //出现断层。
            d[u]=minlevel+1;
            num[d[u]]++;
            u=pre[u];
        }
    }
    return flow;
}

int main()
{
    int n,m,u,v,w;      //m,边数,n,节点数.
    while(~scanf("%d%d",&m,&n)){
        memset(e,0,sizeof(e));
        while(m--){
            scanf("%d%d%d",&u,&v,&w);
            e[u][v]+=w;
        }
        printf("%d\n",SAP(1,n));
    }
    return 0;
}

邻接表

#include <iostream>
#include <string.h>
#include <queue>
using namespace std;

const int INF = 0x3f3f3f3f;
const int MAX_N=205;     //顶点数上限
const int MAX_M=10000;    //总的边数上限

struct edge{
    int v,c,next;       //v指另一个顶点,c表示容量。
}e[MAX_M];

int p[MAX_N],eid;

void init(){
	memset(p,-1,sizeof(p));
    eid=0;
}

void insert(int u,int v,int c){ //插入一条从u向v,容量为c的弧。
    e[eid].v=v;
    e[eid].next=p[u];
    e[eid].c=c;
    p[u]=eid++;
}

void addedge(int u,int v,int c){ //用insert插入网络中的弧
    insert(u,v,c);
    insert(v,u,0);                  //插入一条反方向,当前容量为0的弧
}

int num[MAX_N];
int d[MAX_N];
int cur[MAX_N];
int pre[MAX_N];

int SPA(int s,int t,int n){           //S是源点,T是汇点。
    int cur_flow,flow_ans=0,u,tmp,neck,i;
    memset(num,0,sizeof(num));
    memset(d,0,sizeof(d));
    memset(pre,-1,sizeof(pre));
    for(i=1;i<=n;i++){
        cur[i]=p[i];
    }
    num[0]=n;
    u=s;
    while(d[s]<n){
        if(u==t){
            cur_flow=INF;
            for(i=s;i!=t;i=e[cur[i]].v){
                if(cur_flow>e[cur[i]].c){   //增广成功,寻找瓶颈边
                    neck=i;
                    cur_flow=e[cur[i]].c;
                }
            }
            for(i=s;i!=t;i=e[cur[i]].v){
                tmp=cur[i];
                e[tmp].c-=cur_flow;
                e[tmp^1].c+=cur_flow;
            }
            flow_ans+=cur_flow;
            u=neck; //下次增广从瓶颈边开始
        }
        //寻找可行弧
        for(i=cur[u];i!=-1;i=e[i].next){
            if(e[i].c&&d[u]==d[e[i].v]+1) break;
        }
        if(i!=-1){
            cur[u]=i;
            pre[e[i].v]=u;
            u=e[i].v;
        }else{
            if(0==--num[d[u]]) break;
            cur[u]=p[u];
            for(tmp=n,i=p[u];i!=-1;i=e[i].next){
                if(e[i].c){
                    tmp=min(tmp,d[e[i].v]);
                }
            }
            d[u]=tmp+1;
            num[d[u]]++;
            if(u!=s) u=pre[u];
        }
    }
    return flow_ans;
}

int main() {
	int n,m;
    while(cin>>m>>n){
        init();
        for(int i=0;i<m;i++){
            int u,v,flow;
            cin>>u>>v>>flow;
            addedge(u,v,flow);
        }
        cout<<SPA(1,n,n)<<endl;
    }
    return 0;
}

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值