ACM图算法——最大流(SAP算法)

       其实大致思路就是每次增广最短路径, 维护一个dis数组表示每个点到汇点的最小跳数,dis数组必须有这个性质: dis[i] <= dis[j] + 1 ( r[i][j] > 0); 定义一条允许弧:如果满足:dis[i] = dis[j] + 1( r[i][j] > 0);那么连接i,j的边就叫允许弧, 书上有这个定理:从源点到汇点的最短路一定是用允许弧构成。所以每次扩展路径都找允许弧,如果i没有允许弧就更新dis[i] = min{ dis[j] + 1 | r[i][j] > 0); 如此容易知道当dis[source] = n是就增广完毕,因为从源点到汇点最多有n-1步。最后dis数组可以用一个BFS初始化。

#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;

//c1是c的反向网络,用于dis数组的初始化
int dis[Max];

void initialize()// 初始化dis数组
{
     int q[Max],head = 0, tail = 0;//BFS队列
     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];

    // top是当前增广路中最前面一个点。
    memset(low,0,sizeof(low));//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];//更新low
            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++)//找和top相邻dis最小的点
            {
                if(r[top][j] > 0 && mindis > dis[j] + 1)
                    mindis = dis[j] + 1;
            }
            dis[top] = mindis;//更新top的距离值
            if(top != source) top = pre[top];// 回溯找另外的路
        }
    }
    return(flow);
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值