国内有很多OIER把Improved SAP叫做SAP,说实话这是有些不妥的。因为SAP的定义是以最短路径增值为基础的网络流算法,Dinic算法从广义上讲也是SAP算法的一种。
SAP的思想其实和Dinic很像,不过注重的是对层数标号的维护,而不是像Dinic一样用完一个层次便重新进行广搜。
其基本步骤可以概括为if(起点层次<n){
if(当前节点==n) 增广并将当前节点重设为1;
从当前节点找满足条件的边,
如果存在,则前进一步,将当前节点设为边的下一个点,并设置回路;
否则,则对当前节点进行重标号,并利用之前保存的回路回退一步;
}
代码如下
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#define MAXN 100000
#define INF 0xffffff
using namespace std;
struct list{
int val,p;
list *next,*b;
list(){}
list(int _p,int _val,list *_next)
:p(_p),val(_val),next(_next){}
}T[MAXN*20],*head[MAXN],*now_head[MAXN],*data;
list *rev[MAXN];
int m,n,sum;
int L[MAXN],edge[MAXN];
void ISAP(){
int now;
for(int i=1;i<=n;i++) now_head[i]=head[i];
memset(L,0,sizeof(L));
L[0]=n;now=1;
while(L[1]<n){
//for(int i=1;i<=n;i++) printf("%d ",L[i]);
//cout<<endl;system("pause");
if(now==n){
int delta=INF;
for(int i=1;i!=n;i=now_head[i]->p)
if(now_head[i]->val>0) delta=min(delta,now_head[i]->val);
for(int i=1;i!=n;i=now_head[i]->p)
now_head[i]->val-=delta,now_head[i]->b->val+=delta;//增广
sum+=delta;
now=1;//重设当前节点
}list *k;
for(k=now_head[now];k;k=k->next)
if(k->val&&L[k->p]==L[now]-1) break;//找满足条件的边
if(k){
now_head[now]=k;
rev[now=k->p]=k->b;
}else{
int minf=INF;
if((--edge[L[now]])==0) break; //GAP优化,非常有效
now_head[now]=head[now];
for(list *s=head[now];s;s=s->next) if(s->val) minf=min(minf,L[s->p]);//重标号
++edge[L[now]=minf+1];
if (now!=1) now=rev[now]->p;
}
}
}
int main(){
int x,y,z;
freopen("ditch.in","r",stdin);
freopen("ditch.out","w",stdout);
while(scanf("%d%d",&m,&n)!=EOF){
data=T;
sum=0; memset(head,0,sizeof(head));
memset(now_head,0,sizeof(now_head));
for(int i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
if (x==y) continue;
head[x]=new ((void*) data++) list(y,z,head[x]);
head[y]=new ((void*) data++) list(x,0,head[y]);
head[x]->b=head[y]; head[y]->b=head[x];
}ISAP();
printf("%d\n",sum);
}
return 0;
}