最大流SAP算法:
- 初始弧优化
- 分层图优化
- gap优化
初始弧:
dg中记录一个点走到哪条边了,下次只用接着往后枚举。
分层图优化 :
所谓距离标号dis ,就是某个点到汇点的最少的弧的数量dis(一边走一边求),走的时候只走允许弧(dis[i] = dis[j+1] i----->j)
gap优化:
gap[i]数组表示距离标号为i的点有多少个,如果到某一点没有符合距离标号的允许弧,那么需要修改距离标号来找到增广路; 如果重标号使得gap数组中原标号数目变为0,则算法结束。
struct edge
{ll v,fr,d,t; ll c;};//注意d是不变的,c是剩余流量
ll dfs(ll k,ll flow)
{
if(k==T)return flow;
ll have=0;
for(ll p=cur[k];p;p=e[p].fr)
if(...)
{
ll &v=e[p].v,&c=e[p].c;
if(dis[k] -1 == dis[v] && c)
{
cur[k] = p;
ll now = dfs(v, min(flow - have,c));
c-= now;
e[p^1].c+=now;
have+=now;
if(have == flow)return flow;
}
}
cur[k] = tail[k];
if(!(--gap[dis[k]])) dis[S]=T;
gap[++dis[k]]++;
return have;
}
ll network_flow()
{
mem(gap,0);
mem(dis,0);
for(i=0;i<=T;i++)cur[i] = tail[i];
int ans=0;
while(dis[S] < T) ans+=dfs(S,INF);
return ans;
}
详细地:
在dfs中,有两个形参k,flow分别表示当前流到的点和走到目前的可行流量。
1.如果k==T即走到汇点返回当前这一条增广路的可行流量flow。
2.如果还没到达汇点,尝试尽可能流出这flow的流量,我们用have记录流出的流量。
枚举出边,设出边到达的端点为v ,这条边的剩余流量c。
若这是一条可行边(dis[k] == dis[v] + 1)且剩余流量大于0 ,则尝试流这条边。初始弧更新。
计算通过这条边可以留出的流量。
已知flow那么多的流量还剩下flow - have ,那么显然now = min(flow - have , c) 。
然后更新这条边的剩余流量 , 这条边的反向边加上now,have加上now。
have == flow没东西可以流了 , return flow(全部流完)。
3.还有剩下的流量,尝试将距离标号加1创造机会。
初始弧还原。
如果更新后原标号的个数为0了,出现断层,程序结束。否则更新。
4.看看程序是否结束了(dis[S] == T时结束 , 因为此时已经试完了所有情况),没结束回到dfs 。