增广路方法:
①一般增广路算法O(mnU):采取标号法每次在容量网络中寻找一条增广路(或者在残留网络中,每次任意找一条增广路),知道不存在增广路。
模板:
1 #include <stdio.h> 2 #include <math.h> 3 #include <string.h> 4 #include <iostream> 5 using namespace std; 6 #define M 1000 7 #define INF 100000000 8 int min(int a,int b) 9 { 10 return a<b?a:b; 11 } 12 struct Arc 13 { 14 int c,f; 15 }edge[M][M]; 16 int n,m; 17 int flag[M];//顶点状态:-1未标记 0已标记未检查 1已标号已检查 18 int prev[M];//标号的第一个分量:指明标号从哪个顶点得到,以便找出可改进量 19 int alpha[M];//标号的第二个分量:可改进量α 20 int queue[M];//队列 21 int v,qs,qe;//队列头元素和队列头和队列尾 22 void ford() 23 { 24 while(1)//标号直至不存在可改进路 25 {//标号前对顶点状态数组初始化0xff 26 memset(flag,-1,sizeof(flag)); 27 memset(prev,-1,sizeof(prev)); 28 memset(alpha,-1,sizeof(alpha)); 29 flag[0]=prev[0]=0;//源点为己标号未检查顶点 30 alpha[0]=INF;//源点可流入inf 31 qs=qe=0; 32 queue[qe++]=0;//入队列 33 while(qs<qe&&flag[n-1]==-1)//如果队列未空且最后一个点为-1 34 { 35 v=queue[qs++];//队头元素 36 for(int i=0;i<n;i++)//检查顶点v的正向和反向临接顶点 37 { 38 if(flag[i]==-1)//顶点i未标号 39 { 40 if(edge[v][i].c<INF&&edge[v][i].f<edge[v][i].c) 41 {//正向且未满 42 flag[i]=0;prev[i]=v;//给顶点i标号(已标号未检查) 43 alpha[i]=min(alpha[v],edge[v][i].c-edge[v][i].f); 44 queue[qe++]=i;//顶点i入队列 45 } 46 else if(edge[i][v].c<INF&&edge[i][v].f>0) 47 {//反向且有流量 48 flag[i]=0;prev[i]=-v;//给顶点i标号(已标号未检查) 49 alpha[i]=min(alpha[v],edge[i][v].f); 50 queue[qe++]=i;//顶点i入队列 51 } 52 } 53 } 54 flag[v]=1;//顶点v已标号已检查 55 } 56 if(flag[n-1]==-1||alpha[n-1]==0) break; 57 //当汇点没有获得标号或者汇点的α为0,应该退出while循环 58 int k1=n-1,k2=fabs(prev[k1]); 59 int a=alpha[n-1];//可改进量 60 while(1) 61 { 62 if(edge[k2][k1].f<INF)//正向 63 edge[k2][k1].f=edge[k2][k1].f+a; 64 else edge[k1][k2].f=edge[k1][k2].f-a;//反向 65 if(k2==0) break;//调整一直到源点v0 66 k1=k2; k2=fabs(prev[k2]); 67 } 68 } 69 //输出各条弧及其流量,以及求得的最大流量 70 int maxflow=0; 71 for(int i=0;i<n;i++) 72 { 73 for(int j=0;j<n;j++) 74 { 75 if(i==0&&edge[i][j].f<INF)//求源点流出量,即最大流 76 maxflow+=edge[i][j].f; 77 if(edge[i][j].f<INF) 78 printf("%d-->%d:%d\n",i,j,edge[i][j].f); 79 } 80 } 81 printf("%d\n",maxflow); 82 } 83 int main() 84 { 85 int u,v,c,f; 86 while(scanf("%d%d",&n,&m)!=EOF) 87 { 88 for(int i=0;i<n;i++) 89 { 90 for(int j=0;j<n;j++) 91 edge[i][j].c=edge[i][j].f=INF; 92 } 93 for(int i=0;i<m;i++) 94 { 95 scanf("%d%d%d%d",&u,&v,&c,&f); 96 edge[u][v].c=c; 97 edge[u][v].f=f; 98 } 99 ford(); 100 } 101 }
②最短增广路算法O(nm^2):每个阶段,在层次网络中,不断用BFS算法进行增广直至存在增广路为止,如果汇点不在层次网络中,则算法结束。
poj3498 SAP算法:
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cmath> 5 6 using namespace std; 7 #define INF 2000000000 8 #define min2(a,b) (a<b)?a:b 9 #define M 40005 10 #define N 215 11 struct EDGE{ 12 int v,re,next,w; 13 }edge[M]; 14 int f[M]; 15 int head[N],totaledge,totalpoint; 16 int cnt[N],d[N]; 17 void clear_graph() 18 { 19 totaledge=0; 20 memset(head,-1,sizeof(head)); 21 } 22 void out_graph(int n) 23 { 24 for(int u=0;u<n;u++) 25 { 26 cout<<"for u="<<u<<":"; 27 for(int e=head[u];e!=-1;e=edge[e].next) 28 cout<<""<<edge[e].v<<","<<edge[e].w; 29 cout<<endl; 30 } 31 } 32 void add_edge(int u,int v,int w) 33 { 34 edge[++totaledge].v=v;edge[totaledge].w=w; 35 edge[totaledge].next=head[u];head[u]=totaledge; 36 edge[totaledge].re=totaledge+1; 37 38 edge[++totaledge].v=u;edge[totaledge].w=0; 39 edge[totaledge].next=head[v];head[v]=totaledge; 40 edge[totaledge].re=totaledge-1; 41 } 42 int dfs(int S,int T,int u,int UP) 43 { 44 if(u==T)return UP; 45 int temp=UP,pos=totalpoint-1; 46 for(int e=head[u];e!=-1;e=edge[e].next) 47 { 48 if(d[u]==d[edge[e].v]+1 && edge[e].w>f[e]) 49 { 50 int canflow=dfs(S,T,edge[e].v,min2(UP,edge[e].w-f[e])); 51 UP-=canflow; 52 f[e]+=canflow; 53 f[edge[e].re]-=canflow; 54 if(!UP || d[S]==totalpoint)return temp-UP; 55 } 56 if(edge[e].w>f[e] && pos>d[edge[e].v])pos=d[edge[e].v]; 57 } 58 59 if(UP==temp) 60 { 61 cnt[d[u]]--; 62 if(cnt[d[u]]==0)d[S]=totalpoint; 63 else d[u]=pos+1,cnt[d[u]]++; 64 } 65 return temp-UP; 66 } 67 int SAP(int S,int T) 68 { 69 memset(d,0,sizeof(d)); 70 memset(cnt,0,sizeof(cnt)); 71 int ans=0; 72 while(d[S]<totalpoint) 73 ans+=dfs(S,T,S,INF); 74 return ans; 75 } 76 double p[N][2],r; 77 double dis(int i,int j) 78 { 79 return (p[i][0]-p[j][0])*(p[i][0]-p[j][0]) 80 + (p[i][1]-p[j][1])*(p[i][1]-p[j][1]); 81 } 82 int main() 83 { 84 int a,b,n,T,count,stack[N]; 85 cin>>T; 86 while(T--) 87 { 88 cin>>n>>r; 89 clear_graph(); 90 count=0; 91 for(int i=1;i<=n;i++) 92 { 93 cin>>p[i][0]>>p[i][1]>>a>>b; 94 add_edge(0,i,a); 95 add_edge(i,i+n,b); 96 count+=a; 97 } 98 for(int i=1;i<=n;i++) 99 for(int j=i+1;j<=n;j++) 100 if(dis(i,j)<=r*r) 101 { 102 add_edge(i+n,j,INF); 103 add_edge(j+n,i,INF); 104 } 105 106 totalpoint=2*n+2; 107 stack[0]=0; 108 for(int sink=1;sink<=n;sink++) 109 { 110 memset(f,0,sizeof(f)); 111 if(SAP(0,sink) == count) 112 stack[++stack[0]]=sink-1; 113 } 114 if(stack[0]) 115 { 116 cout<<stack[1]; 117 for(int i=2;i<=stack[0];i++) 118 cout<<" "<<stack[i]; 119 cout<<endl; 120 } 121 else cout<<"-1"<<endl; 122 } 123 124 return 0; 125 }
③连续最短路增广路算法O(n^2m):在最短增广路算法的基础上改造,在每个阶段用一个DFS过程实现多次增广,如果汇点不在层次网络中,则算法结束。
求最大流的本质,就是不停的寻找增广路径。直到找不到增广路径为止。
对于这个一般性的过程,Dinic算法的优化如下:
(1)
Dinic算法首先对图进行一次BFS,然后在BFS生成的层次图中进行多次DFS。层次图的意思就是,只有在BFS树中深度相差1的节点才是连接的。
这就切断了原有的图中的许多不必要的连接。这是需要证明的,估计证明也很复杂。
(2)
除此之外,每次DFS完后,会找到路径中容量最小的一条边。在这条边之前的路径的容量是大于等于这条边的容量的。那么从这条边之前的点,可能引发出别的增广路径。
比如说 S -> b -> c -> d -> T 是一条增广路径,容量最小的边是 b -> c。可能存在一条 S -> b -> e -> f -> g -> T 这样的增广路径。这样的话,在找到第一条增广路径后,只需要回溯到 b 点,就可以继续找下去了。这样做的好处是,避免了找到一条路径就从头开始寻找另外一条的开销。也就是再次从 S 寻找到 b 的开销。这个过程看似复杂,但是代码实现起来很优雅,因为它的本质就是回溯!
(3)
在同一次 DFS 中。如果从一个点引发不出任何的增广路径,就将这个点在层次图中抹去。
POJ3498 Dinic算法:
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cmath> 5 using namespace std; 6 #define INF 2000000000 7 #define min2(a,b) (a<b)?a:b 8 #define M 40005 9 #define N 215 10 struct EDGE{ 11 int v,re,next,w; 12 }edge[M]; 13 int f[M]; 14 int head[N],totaledge,totalpoint; 15 int q[N],d[N]; 16 void clear_graph() 17 { 18 totaledge=0; 19 memset(head,-1,sizeof(head)); 20 } 21 void out_graph(int n) 22 { 23 for(int u=0;u<n;u++) 24 { 25 cout<<"for u="<<u<<":"; 26 for(int e=head[u];e!=-1;e=edge[e].next) 27 cout<<""<<edge[e].v<<","<<edge[e].w; 28 cout<<endl; 29 } 30 } 31 void add_edge(int u,int v,int w) 32 { 33 edge[++totaledge].v=v; 34 edge[totaledge].w=w; 35 edge[totaledge].next=head[u]; 36 head[u]=totaledge; 37 edge[totaledge].re=totaledge+1; 38 39 edge[++totaledge].v=u; 40 edge[totaledge].w=0; 41 edge[totaledge].next=head[v]; 42 head[v]=totaledge; 43 edge[totaledge].re=totaledge-1; 44 } 45 bool bfs(int S,int T) 46 { 47 int u,e,front,rear; 48 memset(d,-1,sizeof(d)); 49 front=rear=0; 50 q[rear++]=S; 51 d[S]=0; 52 while(front!=rear) 53 { 54 u=q[front++];front^=(front==N)?N:0; 55 for(e=head[u];e!=-1;e=edge[e].next) 56 if(edge[e].w>f[e] && d[edge[e].v]==-1) 57 { 58 d[edge[e].v]=d[u]+1; 59 q[rear++]=edge[e].v; 60 rear^=(rear==N)?N:0; 61 } 62 } 63 if(d[T]>=0)return true; 64 else return false; 65 } 66 int dinic(int S,int T,int sum) 67 { 68 if(S==T)return sum; 69 int tp=sum; 70 for(int e=head[S];e!=-1 && sum;e=edge[e].next) 71 if(d[edge[e].v]==d[S]+1 && edge[e].w>f[e]) 72 { 73 int canflow=dinic(edge[e].v,T,min2(sum,edge[e].w-f[e])); 74 f[e]+=canflow; 75 f[edge[e].re]-=canflow; 76 sum-=canflow; 77 } 78 return tp-sum; 79 } 80 81 double p[N][2],r; 82 double dis(int i,int j) 83 { 84 return (p[i][0]-p[j][0])*(p[i][0]-p[j][0]) 85 + (p[i][1]-p[j][1])*(p[i][1]-p[j][1]); 86 } 87 int main() 88 { 89 int a,b,n,T,cnt,stack[N]; 90 cin>>T; 91 while(T--) 92 { 93 cin>>n>>r; 94 clear_graph(); 95 cnt=0; 96 for(int i=1;i<=n;i++) 97 { 98 cin>>p[i][0]>>p[i][1]>>a>>b; 99 if(a) add_edge(0,i,a); 100 add_edge(i,i+n,b); 101 cnt+=a; 102 } 103 for(int i=1;i<=n;i++) 104 for(int j=i+1;j<=n;j++) 105 if(dis(i,j)<=r*r) 106 { 107 add_edge(i+n,j,INF); 108 add_edge(j+n,i,INF); 109 } 110 111 stack[0]=0; 112 for(int sink=1;sink<=n;sink++) 113 { 114 memset(f,0,sizeof(f)); 115 int ans=0; 116 while(bfs(0,sink)) 117 ans+=dinic(0,sink,INF); 118 if(ans==cnt)stack[++stack[0]]=sink-1; 119 } 120 if(stack[0]) 121 { 122 cout<<stack[1]; 123 for(int i=2;i<=stack[0];i++) 124 cout<<" "<<stack[i]; 125 cout<<endl; 126 } 127 else cout<<"-1"<<endl; 128 } 129 130 return 0; 131 }
预留推进算法:
①一般预留推进算法O(n^2m):维护一个预流,不断地对活跃顶点执行推进操作或重标记操作来调整这个预流,直到不能操作。
POJ1459:
1 #include <cmath> 2 #include <queue> 3 #include <cstdio> 4 #include <cstring> 5 #include <iostream> 6 #include <algorithm> 7 using namespace std; 8 const int M=110; 9 const int inf=0x7fffffff; 10 int n,np,nc,m; 11 int resi[M][M]; 12 deque<int> act; 13 int h[M],ef[M]; 14 int s,t,V; 15 void push_relabel() 16 { 17 int i,j; 18 int sum=0; 19 int u,v,p; 20 for(i=1;i<=V;i++) 21 h[i]=0; 22 h[s]=V; 23 memset(ef,0,sizeof(ef)); 24 ef[s]=inf;ef[t]=-inf; 25 act.push_front(s); 26 while(!act.empty()) 27 { 28 u=act.back(); 29 act.pop_back(); 30 for(i=1;i<=V;i++) 31 { 32 v=i; 33 if(resi[u][v]<ef[u]) 34 p=resi[u][v]; 35 else 36 p=ef[u]; 37 if(p>0&&(u==s||h[u]==h[v]+1)) 38 { 39 resi[u][v]-=p; 40 resi[v][u]+=p; 41 if(v==t) sum+=p; 42 ef[u]-=p; 43 ef[v]+=p; 44 if(v!=s&&v!=t) 45 act.push_front(v); 46 } 47 } 48 if(u!=s&&u!=t&&ef[u]>0) 49 { 50 h[u]++; 51 act.push_front(u); 52 } 53 } 54 printf("%d\n",sum); 55 } 56 int main() 57 { 58 int i,j,u,v,val; 59 while(scanf("%d%d%d%d",&n,&np,&nc,&m)!=EOF) 60 { 61 s=n+1; 62 t=V=n+2; 63 memset(resi,0,sizeof(resi)); 64 for(i=0;i<m;i++) 65 { 66 while(getchar()!='('); 67 scanf("%d,%d)%d",&u,&v,&val); 68 resi[u+1][v+1]=val; 69 } 70 for(i=0;i<np;i++) 71 { 72 while(getchar()!='('); 73 scanf("%d)%d",&u,&val); 74 resi[s][u+1]=val; 75 } 76 for(i=0;i<nc;i++) 77 { 78 while(getchar()!='('); 79 scanf("%d)%d",&u,&val); 80 resi[u+1][t]=val; 81 } 82 push_relabel(); 83 } 84 return 0; 85 }
②最高标号预流推进算法O(n^2m^(1/2)):每次检查具有最高标号的活跃结点。