网络流之SAP算法学习
终于决定开始学习网络流了=.=
<<图论算法理论、实践与应用>>那本书讲了很多关于求最大流的算法,然后我就只挑了一种传说中神奇的SAP算法学习。
首先引入几个新名词:
1、距离标号:
所谓距离标号 ,就是某个点到汇点的最少的弧的数量(即边权值为1时某个点到汇点的最短路径长度)。
设点i的标号为level[i],那么如果将满足level[i]=level[j]+1的弧(i,j)叫做允许弧 ,且增广时只走允许弧。
2、断层(本算法的Gap优化思想):
gap[i]数组表示距离标号为i的点有多少个,如果到某一点没有符合距离标号的允许弧,那么需要修改距离标号来找到增广路;
如果重标号使得gap数组中原标号数目变为0,则算法结束。
SAP算法框架:
1、初始化;
2、不断沿着可行弧找增广路。可行弧的定义为{( i , j ) , level[i]==level[j]+1};
3、当前节点遍历完以后,为了保证下次再来的时候有路可走,重新标号当前距离,level[i]=min(level[j]+1);
该算法最重要的就是gap常数优化了。
下面对hdu 1532贴上模版:
http://acm.hdu.edu.cn/showproblem.php?pid=1532
邻接矩阵:

1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 #define MAXN 222 6 #define inf 100000000+1000 7 int map[MAXN][MAXN];//存图 8 int pre[MAXN];//记录当前点的前驱 9 int level[MAXN];//记录距离标号 10 int gap[MAXN];//gap常数优化 11 int NV,NE; 12 13 //入口参数vs源点,vt汇点 14 int SAP(int vs,int vt){ 15 memset(pre,-1,sizeof(pre)); 16 memset(level,0,sizeof(level)); 17 memset(gap,0,sizeof(gap)); 18 gap[0]=vt; 19 int v,u=pre[vs]=vs,maxflow=0,aug=inf; 20 while(level[vs]<vt){ 21 //寻找可行弧 22 for(v=1;v<=vt;v++){ 23 if(map[u][v]>0&&level[u]==level[v]+1){ 24 break; 25 } 26 } 27 if(v<=vt){ 28 pre[v]=u; 29 u=v; 30 if(v==vt){ 31 aug=inf; 32 //寻找当前找到的一条路径上的最大流 33 for(int i=v;i!=vs;i=pre[i]){ 34 if(aug>map[pre[i]][i])aug=map[pre[i]][i]; 35 } 36 maxflow+=aug; 37 //更新残留网络 38 for(int i=v;i!=vs;i=pre[i]){ 39 map[pre[i]][i]-=aug; 40 map[i][pre[i]]+=aug; 41 } 42 u=vs;//从源点开始继续搜 43 } 44 }else { 45 //找不到可行弧 46 int minlevel=vt; 47 //寻找与当前点相连接的点中最小的距离标号 48 for(v=1;v<=vt;v++){ 49 if(map[u][v]>0&&minlevel>level[v]){ 50 minlevel=level[v]; 51 } 52 } 53 gap[level[u]]--;//(更新gap数组)当前标号的数目减1; 54 if(gap[level[u]]==0)break;//出现断层 55 level[u]=minlevel+1; 56 gap[level[u]]++; 57 u=pre[u]; 58 } 59 } 60 return maxflow; 61 } 62 63 int main(){ 64 int n,m,u,v,cap; 65 while(~scanf("%d%d",&m,&n)){ 66 memset(map,0,sizeof(map)); 67 for(int i=1;i<=m;i++){ 68 scanf("%d%d%d",&u,&v,&cap); 69 map[u][v]+=cap; 70 } 71 printf("%d\n",SAP(1,n)); 72 } 73 return 0; 74 }