初识网络流
研究网络流快半个月了,只觉得自己学的太慢。学长说模板什么的,码多了就明白什么意思了,可咱总是想先了解清楚不是。。。
昨天校赛上,一道费用流把咱坑住了,这不是还没来得及看么,被队友好一顿数落 T~T
今天又花了三个小时才把sap的模板看明白了。。。人家学网络流都是拿着模板上来就做题,差距啊 O(∩_∩)O哈哈~
下面晒一晒学习成果
EK:十分暴力,不过刚上手还是从简单的来吧
Edmond-Karp
1 #define N 1000 2 #define Inf 9999999 3 4 int G[N][N],c[N][N],f[N][N],pre[N],que[N],n,vis[N]; 5 6 int ek(int s,int t) 7 { 8 int i,j,k,head,tail,flow=0; 9 memset(f,0,sizeof(f)); 10 while(true) 11 { 12 head=tail=0; 13 memset(vis,0,sizeof(vis)); 14 que[tail++]=s; 15 vis[s]=true; 16 while(head<tail) //寻找 增广路 17 { 18 k=que[head++]; 19 if(k==t) 20 break; 21 for(i=1;i<=n;i++) 22 { 23 if(c[k][i]>0&&!vis[i]) // 入队:沿有向边方向dfs汇点t 24 { 25 vis[i]=true; 26 pre[i]=k; 27 que[tail++]=i; 28 } 29 } 30 } 31 if(k!=t) //如果找不到增广路:结束 32 break; 33 int cc=Inf; // cc:增广路上边权的最小值 34 j=t; 35 i=pre[j]; 36 while(j!=s) //初走增广路:获取最小值 37 { 38 if(c[i][j]<cc) 39 cc=c[i][j]; 40 j=i; 41 i=pre[j]; 42 } 43 flow+=cc; //flow:当前最大流 44 j=t; 45 i=pre[j]; 46 while(j!=s) //重走增广路:更新残流、反向边 47 { 48 f[i][j]+=cc; 49 f[j][i]=-f[i][j]; 50 c[i][j]=G[i][j]-f[i][j]; 51 c[j][i]=G[j][i]-f[j][i]; 52 j=i; 53 i=pre[j]; 54 } 55 } 56 return flow; 57 }
dinic:引入了层次的概念,不用担心“绕圈的”被无视了
dinic
1 #define Min(a,b) a<b?a:b 2 #define N 10000 3 #define Inf 9999999 4 struct Edge 5 { 6 int to,cap,opt,next; 7 }e[100000]; 8 int ec,pp[N],n,s,t; //pp[]:邻接表头表 9 int level[N],que[N]; 10 11 bool makelevel() //标记层次 12 { 13 int i,j,k,head=0,tail=0; 14 memset(level,-1,sizeof(level)); 15 level[s]=0; 16 que[tail++]=s; 17 while(head<tail) 18 { 19 k=que[head++]; 20 for(i=pp[k];i!=-1;i=e[i].next) 21 if(e[i].cap>0&&level[e[i].to]==-1) //遍历队从队首节点出发的边&未标记等级的:等级+1,并入队 22 { 23 level[e[i].to]=level[k]+1; 24 que[tail++]=e[i].to; 25 } 26 } 27 return level[t]!=-1; //若汇点t未被标记 28 } 29 30 int findpath(int u,int alpha) // DFS:从源点到汇点,参数:当前点,这条路上的最小值 31 { 32 if(u==t) 33 return alpha; 34 int i,j,k,w=0; 35 for(i=pp[u];i!=-1;i=e[i].next) 36 if(e[i].cap>0&&level[e[i].to]==level[u]+1) 37 if(k=findpath(e[i].to,Min(e[i].cap,alpha-w))) 38 { 39 e[i].cap-=k; 40 e[e[i].opt].cap+=k; 41 w+=k; 42 } 43 return w; 44 } 45 46 int dinic() 47 { 48 int i,j,k=0; 49 while(makelevel()) 50 while(i=findpath(s,Inf)) //在增广路存在时,i=增广路上的最小值;否则,无增广路,输出k 51 while(makelevel())不会无限标记 52 k+=i; 53 return k; 54 }
dinic的常数优化:走岔路。BFS+DFS,思路很清晰
源自http://happylch21.blog.163.com/blog/static/16563975920116259323343/
View Code
1 #include<stdio.h> 2 #include<string.h> 3 4 #define MIN(a,b) a > b ? b : a 5 #define NUM 250 6 #define INF 0x7ffffff 7 8 typedef struct { 9 int e,f,next; 10 }info; 11 info edge[NUM * NUM]; 12 13 int head[NUM]; 14 int Que[NUM]; 15 int level[NUM]; 16 int st,ed; 17 int tol; 18 19 void Add(int s,int t,int c) //构建邻接表 20 { 21 edge[tol].e = t; 22 edge[tol].f = c; 23 edge[tol].next = head[s]; 24 head[s] = tol ++; 25 26 edge[tol].e = s; //构建反向边 27 edge[tol].f = 0; 28 edge[tol].next = head[t]; 29 head[t] = tol ++; 30 } 31 32 int BFS() 33 { 34 int fr,tp,next,cur_level,cur; 35 memset(level,-1,sizeof(level)); 36 Que[0] = st; 37 level[st] = 0; 38 for( fr = 0,tp = 1; fr != tp; fr = (fr + 1)%NUM ){ 39 cur = Que[fr]; 40 cur_level = level[cur]; 41 for(next = head[cur]; next != -1; next = edge[next].next){ 42 if( edge[next].f && level[ edge[next].e ] == -1 ){ 43 Que[tp] = edge[next].e; 44 level[ edge[next].e ] = cur_level + 1; 45 tp = (tp + 1)%NUM; 46 } 47 } 48 } 49 return level[ed] != -1; 50 } 51 52 int DFS(int s,int min) 53 { 54 int r = 0,next; // r 是 "亮点" 55 int t; 56 if( s == ed ) return min; 57 for( next = head[s]; r < min && next != -1;next = edge[next].next ){ 58 if( edge[next].f && level[s]+1== level[ edge[next].e ] ){ 59 t = MIN( min - r,edge[next].f ); // 取当前路得最小值 60 // 第一次经过 (无岔路) r = 0 <=> ( min - r )到目前为止各边的最小值 ; edge[next].f 当前边权 61 // 再次经过 (有岔路) r!=0 <=> r 是已增广的最大流 62 // (min-r)<=> 到当前路为止仍能通过的最大残流 63 t = DFS(edge[next].e,t); 64 r += t; 65 edge[next].f -= t; 66 edge[next^1].f += t; 67 } 68 } 69 if( !r )level[s] = -2; 70 return r; 71 }
sap:反正看上去好复杂,标记层次+寻找增广路+优化用的cur[],全都到一块了,膜拜设计模板的大牛
sap
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #include <algorithm> 5 #include <iostream> 6 using namespace std; 7 const int maxn = 210; 8 const int maxm = 1123123; 9 const int inf = 1000000009; 10 struct E { 11 int v, next, c; 12 }e[maxn*maxn<<2]; 13 int tot, head[maxn]; 14 int n, m; 15 16 void init() { 17 tot = 0; 18 memset(head, -1, sizeof(head)); 19 } 20 21 void add(int s, int t, int c) { //构建邻接表 22 e[tot].v = t; 23 e[tot].c = c; 24 e[tot].next = head[s]; 25 head[s] = tot++; 26 e[tot].v = s; 27 e[tot].c = 0; 28 e[tot].next = head[t]; 29 head[t] = tot++; 30 } 31 32 int gap[maxn]; // gap[]: 记录各层次点的数量 33 int dis[maxn]; // dis[]: 记录各点的层次 34 int pre[maxn]; // pre[]: 记录当前点的前驱 35 int cur[maxn]; // cur[]: 邻接表头表 head[]的替身,通过不断更新,并配合条件 e[i].c>0,寻找最近的允许边 36 37 int sap(int s, int t, int n) // s 源点,t汇点,n顶点总数 38 { 39 int i; 40 for(i = 0; i <= n; i++) { 41 dis[i] = gap[i] = 0; 42 cur[i] = head[i]; 43 } 44 gap[0] = n; //初始化 所有点的层次都是 0 45 int u = pre[s] = s, maxf = 0, aug = inf, v; 46 while(dis[s] < n) 47 { 48 loop: for(i = cur[u]; i != -1; i = e[i].next) // "部分" 遍历邻接表 注意: cur[u]=i(见54行) 头表不断在更新 49 { 50 v = e[i].v; 51 if(e[i].c > 0 && dis[u] == dis[v] + 1) { 52 aug = min(aug, e[i].c); //为了能使 u==t (见56行) 这一条件满足时直接进行增广,aug取所有允许边中的最小值。同时,体现了 cur[u]不断维护的优点,可以立刻再次沿增广路增广,若已完成增广,会跳过 不允许边 ,继续操作 53 pre[v] = u; 54 cur[u] = i; //在邻接表中顺序靠前的:包括当前节点若存在层次较高(与父节点层次相同)的兄弟节点,或边权为零(未增广的反向边,已增广的正向边),做此操作,等价于忽略其之前的兄弟节点 55 u = v; 56 if(u == t) { 57 while(u != s) { 58 u = pre[u]; 59 e[cur[u]].c -= aug; 60 e[cur[u] ^ 1].c += aug; 61 } 62 maxf += aug; 63 aug = inf; 64 } 65 goto loop; 66 } 67 } 68 int min_d = n; 69 for(i = head[u]; i != -1; i = e[i].next) { 70 v = e[i].v; 71 if(e[i].c > 0 && dis[v] < min_d) { 72 min_d = dis[v]; 73 cur[u] = i; // 与 cur[u]=i(见54行) 对应 ,更新cur[u]的值,使邻接表能够从最近的允许边开始遍历 74 } 75 } 76 if(!(--gap[dis[u]])) // 出现断层,结束sap 77 break; 78 ++gap[dis[u] = min_d + 1]; 79 u = pre[u]; 80 } 81 return maxf; 82 }
建议打印一下,了解各个变量的变化。提供白书上的网络流数据
6 10 5 1 16 5 2 13 1 2 10 2 1 4 1 3 12 2 4 14 3 2 9 4 3 7 4 6 4 3 6 20 5 6