网络流之SAP算法

在SAP算法中,我们定义每个顶点的距离标号(Distance Labels),即残留网络中这个点到汇点的距离。然后,我们只在距离标号相邻的点间寻找增广路径。如果从一个点出发没有容许边,就对重标记并回溯(类似预流推进)。首先介绍一些定义:
残留容量:容量-流量。即,残留容量r[i,j]=c[i,j]-f[i,j]。如果只求流值,则在网络流算法中只需记录残量,而不必记录容量和流量。
残留边、残留网络:有残留容量的边成为残留边,由网络的点集、残留边集构成残留网络。
距离标号 :对于每个点i,定义其在当前残留网络中到汇点的距离为d[i]。如d[t]=0。
容许边:当且仅当r[i,j]>0且d[i]=d[j]+1时,边(i,j)是容许边。我们的算法只考虑容许边。

算法的一开始,我们从汇点进行一次BFS求出距离标号。然后从源点开始递归操作。对于一个点i的操作,如果存在容许边(i,j),当j是汇点时增广并从源点开始递归,否则就令i是j的前驱,并递归j。否则对i重标号,使得d[i]=min{d[j]}+1 ( r[i,j] )>0,并回溯。当d[s]>=n时算法停止,此时不存在任何一条增广路。
模板一
#include <iostream>

#include <cstring>

#include <cstdlib>

using namespace std;

const int Max = 225;

const int oo = 210000000;

int n,m,c[Max][Max],r[Max][Max],c1[Max][Max],source,sink;

int dis[Max];

void initialize()

{

     int q[Max],head = 0, tail = 0;

     memset(dis,0,sizeof(dis));

     q[++head] = sink;

     while(tail < head)

     {

         int u = q[++tail], v;

         for(v = 0; v <= sink; v++)

         {

               if(!dis[v] && c1[u][v] > 0)

               {

                   dis[v] = dis[u] + 1;

                   q[++head] = v;

               }

         }

     }

}

int maxflow_sap()

{

    initialize();

    int top = source, pre[Max], flow = 0, i, j, k, low[Max];

 

    memset(low,0,sizeof(low));

    while(dis[source] < n)

    {

        bool flag = false;

        low[source] = oo;

        for(i = 0; i <= sink; i++)

        {

            if(r[top][i] > 0 && dis[top] == dis[i] + 1)

            {

                flag = true;

                break;

            }

        }

        if(flag)

        {

            low[i] = r[top][i];

            if(low[i] > low[top]) low[i] = low[top];

            pre[i] = top; top = i;

            if(top == sink)

            {

                flow += low[sink];

                j = top;

                while(j != source)

                {

                    k = pre[j];

                    r[k][j] -= low[sink];

                    r[j][k] += low[sink];

                    j = k;

                }

                top = source;

                memset(low,0,sizeof(low));

            }

        }

        else

        {

            int mindis = 10000000;

            for(j = 0; j <= sink; j++)

            {

                if(r[top][j] > 0 && mindis > dis[j] + 1)

                    mindis = dis[j] + 1;

            }

            dis[top] = mindis;

            if(top != source) top = pre[top];

        }

    }

    return(flow);

}

模板二
#include <iostream>
#include <queue>
#define msize 1024      //最大顶点数目
using namespace std;
 
int d[msize];           //标号
int r[msize][msize];    //残留网络,初始为原图
int num[msize];         //num[i]表示标号为i的顶点数有多少
int pre[msize];         //记录前驱 
int n,m,s,t;            //m个顶点,n条边,从源点s到汇点t
 //此处别忘了在使用最大流之前先调用此函数初始化标号
void ini_d() //BFS计算标号,汇点t标号为0
{
    int k;
    queue<int>Q;
 
    memset(d,1,sizeof(d));          //将距离设置成为无穷大,此处亦可以使用for循环实现 
    memset(num,0,sizeof(num));
 
    Q.push(t);
    d[t]=0;                   //汇点的标号为0 
    num[0]=1;
    while (!Q.empty())
    {
        k=Q.front(),Q.pop();
        for (int i=0;i<m;i++)       //遍历所有的结点 
        {
            if (d[i]>=m&&r[i][k]>0) //此处要特别注意,通过frontint的值改变其他的距离标号
            {
                d[i]=d[k]+1;
                Q.push(i);
                num[d[i]]++;
            }
        }
    }
}
 
int findAlowArc(int i)       //从i出发寻找允许弧
{
    int j;
    for (j=0;j<m;j++) if (r[i][j]>0&&d[i]==d[j]+1) return j;
 
    return -1;
}
 
int reLable(int i)         //重新标号
{
    int mm=INT_MAX;
    for (int j=0;j<m;j++)
        if (r[i][j]>0) mm=min(mm,d[j]+1);
 
    return mm==INT_MAX?m:mm;
}
 
int maxFlow(int s,int t)      //从源点s出发的最大流
{
    int flow=0,i=s,j;
    int delta;              //增量
 
    memset(pre,-1,sizeof(pre));
    while (d[s]<m)
    {
        j=findAlowArc(i);
        if (j>=0)
        {
            pre[j]=i;                     
            i=j;                //从前往后找 
            if (i==t)           //更新残留网络
            {
                delta=INT_MAX;
                for (i=t;i!=s;i=pre[i]) delta=min(delta,r[pre[i]][i]); //找到增广路径的增量 
                for (i=t;i!=s;i=pre[i]) r[pre[i]][i] -= delta, r[i][pre[i]] += delta; //更改流量 
                flow += delta;
            }
        }
        else
        {
            int x=reLable(i);       //重新标号
            num[x]++;
            num[d[i]]--;
            if (num[d[i]]==0) return flow;      //间隙优化
            d[i]=x;
            if (i!=s) i=pre[i];
        }
    } 
    return flow;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值