終於決定開始學習網絡流了=.=
<<圖論算法理論、實踐與應用>>那本書講了很多關於求最大流的算法,然后我就只挑了一種傳說中神奇的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 } 75 76 77 78 79 80 81 82
鄰接表:

1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 #define MAXN 444 //鄰接表要開邊數的2倍 7 8 struct Edge{ 9 int v,cap,next; 10 }edge[MAXN]; 11 int level[MAXN];//標記層次(距離標號) 12 13 //間隙優化,定義gap[i]為標號是i的點的個數 14 //在重標記i時,檢查gap[level[i]],若減為0,這算法結束。 15 int gap[MAXN]; 16 17 int pre[MAXN];//前驅 18 int cur[MAXN]; 19 int head[MAXN]; 20 int NV,NE; 21 22 //NE為邊數,初始化為0; 23 void Insert(int u,int v,int cap,int cc=0){ 24 edge[NE].cap=cap;edge[NE].v=v; 25 edge[NE].next=head[u];head[u]=NE++; 26 27 edge[NE].cap=cc;edge[NE].v=u; 28 edge[NE].next=head[v];head[v]=NE++; 29 } 30 31 32 //參數,源點,匯點 33 int SAP(int vs,int vt){ 34 memset(level,0,sizeof(level)); 35 memset(pre,-1,sizeof(pre)); 36 memset(gap,0,sizeof(gap)); 37 //cur[i]保存的是當前弧 38 for(int i=0;i<=NV;i++)cur[i]=head[i]; 39 int u=pre[vs]=vs;//源點的pre還是其本身 40 int maxflow=0,aug=-1; 41 gap[0]=NV; 42 while(level[vs]<NV){ 43 loop : 44 for(int &i=cur[u];i!=-1;i=edge[i].next){ 45 int v=edge[i].v;//v是u的后繼 46 //尋找可行弧 47 if(edge[i].cap&&level[u]==level[v]+1){ 48 //aug表示增廣路的可改進量 49 aug==-1?(aug=edge[i].cap):(aug=min(aug,edge[i].cap)); 50 pre[v]=u; 51 u=v; 52 //如果找到一條增廣路 53 if(v==vt){ 54 maxflow+=aug;//更新最大流; 55 //路徑回溯更新殘留網絡 56 for(u=pre[v];v!=vs;v=u,u=pre[u]){ 57 //前向弧容量減少,反向弧容量增加 58 edge[cur[u]].cap-=aug; 59 edge[cur[u]^1].cap+=aug; 60 } 61 aug=-1; 62 } 63 goto loop; 64 } 65 } 66 int minlevel=NV; 67 //尋找與當前點相連接的點中最小的距離標號(重標號) 68 for(int i=head[u];i!=-1;i=edge[i].next){ 69 int v=edge[i].v; 70 if(edge[i].cap&&minlevel>level[v]){ 71 cur[u]=i;//保存弧 72 minlevel=level[v]; 73 } 74 } 75 if((--gap[level[u]])==0)break;//更新gap數組后如果出現斷層,則直接退出。 76 level[u]=minlevel+1;//重標號 77 gap[level[u]]++;//距離標號為level[u]的點的個數+1; 78 u=pre[u];//轉當前點的前驅節點繼續尋找可行弧 79 } 80 return maxflow; 81 } 82 83 int main(){ 84 int m;//邊的條數 85 while(~scanf("%d%d",&m,&NV)){ 86 memset(head,-1,sizeof(head)); 87 NE=0; 88 for(int i=1;i<=m;i++){ 89 int u,v,cap; 90 scanf("%d%d%d",&u,&v,&cap); 91 Insert(u,v,cap); 92 } 93 printf("%d\n",SAP(1,NV)); 94 } 95 return 0; 96 }