其实大致思路就是每次增广最短路径, 维护一个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);
}