[转载 ]POJ 1273 最大流模板
百度文库花了5分下的 不过确实是自己需要的东西
经典的最大流题POJ1273
——其他练习题 POJ3436 、
题意描述:
现在有m个池塘(从1到m开始编号,1为源点,m为汇点),及n条水渠,给出这n条水渠所连接的池塘和所能流过的水量,求水渠中所能流过的水的最大容量.一道基础的最大流题目。但是模板小心使用,目前只是求单源点的最大流。
参考数据:
输入:
5 4
1 2 40
1 4 20
2 4 20
2 3 30
3 4 10
输出:
50
程序实现:
增广路算法Edmonds_Karp
1 //poj1273_Ek.cpp 2 #include<iostream> 3 #include<queue> 4 using namespace std; 5 const int N=201; 6 const int INF=99999999; 7 int n,m,sum,s,t;//s,t为始点和终点 8 int flow[N][N],cap[N][N],a[N],p[N]; 9 //分别为:flow[u][v]为<u,v>流量、cap[u][v]为<u,v>容量、a[i]表示源点s到节点i的路径上的最小残留量、p[i]记录i的前驱 10 int min(int a,int b) 11 { 12 return a<=b?a:b; 13 } 14 void Edmonds_Karp() 15 { 16 int i,u,v; 17 queue<int>q;//队列,用bfs找增广路 18 while(1) 19 { 20 memset(a,0,sizeof(a));//每找一次,初始化一次 21 a[s]=INF; 22 q.push(s);//源点入队 23 while(!q.empty()) 24 { 25 u=q.front(); 26 q.pop(); 27 for(v=1;v<=m;v++) 28 { 29 if(!a[v]&&flow[u][v]<cap[u][v]) 30 { 31 p[v]=u; 32 q.push(v); 33 a[v]=min(a[u],cap[u][v]-flow[u][v]);//s-v路径上的最小残量 34 } 35 } 36 } 37 if(a[m]==0)//找不到增广路,则当前流已经是最大流 38 break; 39 sum+=a[m];//流加上 40 for(i=m;i!=s;i=p[i])// //从汇点顺着这条增广路往回走 41 { 42 flow[p[i]][i]+=a[m];//更新正向流量 43 flow[i][p[i]]-=a[m];//更新反向流量 44 } 45 } 46 printf("%d\n",sum); 47 } 48 int main() 49 { 50 //freopen("in.txt","r",stdin); 51 int v,u,w; 52 while(scanf("%d%d",&n,&m)!=EOF) 53 { 54 s=1;//从1开始 55 t=m;//m为汇点 56 sum=0;//记录最大流量 57 memset(flow,0,sizeof(flow));//初始化 58 memset(cap,0,sizeof(cap)); 59 while(n--) 60 { 61 scanf("%d%d%d",&u,&v,&w); 62 cap[u][v]+=w;//注意图中可能出现相同的边 63 } 64 Edmonds_Karp(); 65 } 66 return 0; 67 }
空间优化:
在程序实现的时候,我们通常只是用一个cap数组来记录容量,而不记录流量,当流量+1的时候,我们可以通过容量-1来实现,以方便程序的实现。正向用cap[u][v],则反向用cap[v][u]表示。
1 #include<iostream> 2 #include<queue> 3 using namespace std; 4 const int N=201; 5 const int INF=99999999; 6 int n,m,sum,s,t,w;//s,t为始点和终点 7 int cap[N][N],a[N],p[N]; 8 int min(int a,int b) 9 { 10 return a<=b?a:b; 11 } 12 void Edmonds_Karp() 13 { 14 int i,u,v; 15 queue<int>q;//队列,用bfs找增广路 16 while(1) 17 { 18 memset(a,0,sizeof(a));//每找一次,初始化一次 19 a[s]=INF; 20 q.push(s);//源点入队 21 while(!q.empty()) 22 { 23 u=q.front(); 24 q.pop(); 25 for(v=1;v<=m;v++) 26 { 27 if(!a[v]&&cap[u][v]>0) 28 { 29 p[v]=u; 30 q.push(v); 31 a[v]=min(a[u],cap[u][v]);//s-v路径上的最小残量 32 } 33 } 34 } 35 if(a[m]==0)//找不到增广路,则当前流已经是最大流 36 break; 37 sum+=a[m];//流加上 38 for(i=m;i!=s;i=p[i])// //从汇点顺着这条增广路往回走 39 { 40 cap[p[i]][i]-=a[m];//更新正向流量 41 cap[i][p[i]]+=a[m];//更新反向流量 42 } 43 } 44 printf("%d\n",sum); 45 } 46 int main() 47 { 48 // freopen("in.txt","r",stdin); 49 int v,u; 50 while(scanf("%d%d",&n,&m)!=EOF) 51 { 52 s=1;//从1开始 53 t=m;//m为汇点 54 sum=0;//记录最大流量 55 memset(cap,0,sizeof(cap));//初始化 56 while(n--) 57 { 58 scanf("%d%d%d",&u,&v,&w); 59 cap[u][v]+=w;//注意图中可能出现相同的边 60 } 61 Edmonds_Karp(); 62 } 63 return 0; 64 }
Dinic邻接矩阵实现
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <iostream> 5 #define min(x,y) ((x<y)?(x):(y)) 6 using namespace std; 7 const int MAX=0x5fffffff;// 8 int tab[250][250];//邻接矩阵 9 int dis[250];//距源点距离,分层图 10 int q[2000],h,r;//BFS队列 ,首,尾 11 int N,M,ANS;//N:点数;M,边数 12 int BFS() 13 { int i,j; 14 memset(dis,0xff,sizeof(dis));//以-1填充 15 dis[1]=0; 16 h=0;r=1; 17 q[1]=1; 18 while (h<r) 19 { 20 j=q[++h]; 21 for (i=1;i<=N;i++) 22 if (dis[i]<0 && tab[j][i]>0) 23 { 24 dis[i]=dis[j]+1; 25 q[++r]=i; 26 } 27 } 28 if (dis[N]>0) 29 return 1; 30 else 31 return 0;//汇点的DIS小于零,表明BFS不到汇点 32 } 33 //Find代表一次增广,函数返回本次增广的流量,返回0表示无法增广 34 int find(int x,int low)//Low是源点到现在最窄的(剩余流量最小)的边的剩余流量 35 { 36 int i,a=0; 37 if (x==N)return low;//是汇点 38 for (i=1;i<=N;i++) 39 if (tab[x][i] >0 //联通 40 && dis[i]==dis[x]+1 //是分层图的下一层 41 &&(a=find(i,min(low,tab[x][i]))))//能到汇点(a <> 0) 42 { 43 tab[x][i]-=a; 44 tab[i][x]+=a; 45 return a; 46 } 47 return 0; 48 } 49 int main() 50 { 51 //freopen("ditch.in" ,"r",stdin ); 52 //freopen("ditch.out","w",stdout); 53 int i,j,f,t,flow,tans; 54 while (scanf("%d%d",&M,&N)!=EOF){ 55 memset(tab,0,sizeof(tab)); 56 for (i=1;i<=M;i++) 57 { 58 scanf("%d%d%d",&f,&t,&flow); 59 tab[f][t]+=flow; 60 } 61 // 62 ANS=0; 63 while (BFS())//要不停地建立分层图,如果BFS不到汇点才结束 64 { 65 while(tans=find(1,0x7fffffff))ANS+=tans;//一次BFS要不停地找增广路,直到找不到为止 66 } 67 printf("%d\n",ANS); 68 } 69 //system("pause"); 70 }
Dinic邻接表实现
1 //#define LOCAL 2 #include <stdio.h> 3 #include <string.h> 4 #include <queue> 5 using namespace std; 6 7 #define MAXN 210 8 #define INF 0x3f3f3f3f 9 10 struct Edge 11 { 12 int st, ed; 13 int c; 14 int next; 15 }edge[MAXN << 1]; 16 17 int n, m; 18 int s, t; 19 int ans; 20 int e = 0; 21 int head[MAXN]; 22 int d[MAXN]; 23 24 int min(int a, int b) 25 { 26 return a < b ? a : b; 27 } 28 29 void init() 30 { 31 int i, j; 32 int a, b, c; 33 s = 1; 34 t = m; 35 e = 0; 36 ans = 0; 37 memset(head, -1, sizeof(head)); 38 for(i = 1; i <= n; i++) 39 { 40 scanf("%d%d%d", &a, &b, &c); 41 edge[e].st = a; 42 edge[e].ed = b; 43 edge[e].c = c; 44 edge[e].next = head[a]; 45 head[a]= e++; 46 edge[e].st = b; 47 edge[e].ed = a; 48 edge[e].next = head[b]; 49 head[b] = e++; 50 } 51 } 52 53 int bfs() 54 { 55 memset(d, -1, sizeof(d)); 56 queue<int> q; 57 d[s] = 0; 58 q.push(s); 59 int i; 60 int cur; 61 while(!q.empty()) 62 { 63 cur = q.front(); 64 q.pop(); 65 for(i = head[cur]; i != -1; i = edge[i].next) 66 { 67 if(d[edge[i].ed] == -1 && edge[i].c > 0) 68 { 69 d[edge[i].ed] = d[cur] + 1; 70 q.push(edge[i].ed); 71 } 72 } 73 } 74 if(d[t] < 0) 75 return 0; 76 return 1; 77 } 78 79 int dinic(int x, int flow) 80 { 81 if(x == t) 82 return flow; 83 int i, a; 84 for(i = head[x]; i != -1; i = edge[i].next) 85 { 86 if(d[edge[i].ed] == d[x] + 1 && edge[i].c > 0 && (a = dinic(edge[i].ed, min(flow, edge[i].c)))) 87 { 88 edge[i].c -= a; 89 edge[i ^ 1].c += a; 90 return a; 91 } 92 } 93 return 0; 94 } 95 96 void solve() 97 { 98 while(scanf("%d%d", &n, &m) != EOF) 99 { 100 init(); 101 while(bfs()) 102 { 103 int increment; 104 increment = dinic(1, INF); 105 ans += increment; 106 } 107 printf("%d\n", ans); 108 } 109 } 110 111 int main() 112 { 113 #ifdef LOCAL 114 freopen("poj1273.txt", "r", stdin); 115 // freopen(".txt", "w", stdout); 116 #endif 117 solve(); 118 return 0; 119 }
SAP实现:poj1273_SAP.cpp
/*
编者感悟:
sap学了很长时间,一直不敢下手写,结果就是不写永远不会真正的理解算法的含义,sap理论很多算法书上都有讲解,但我还是建议看数学专业 图论的书,比如 《有向图的理论,算法及应用》,这本书的内容非常棒,相信看过的都知道吧,比其他算法书上讲的透多了。
SAP有个优化就是 当出现断链时,就可以直接退出,还有个优化是当前弧的优化,这两个优化只需要一句话+一个数组就解决了,相当实惠,好的ISAP执行的效率真的非常高,我写的ISAP用的是链式前向星法表示。
这个就算是模板了吧,虽然写的不是很好。。
*/
1 #include<iostream> 2 #include<cstdio> 3 #include<memory.h> 4 #include<cmath> 5 using namespace std; 6 #define MAXN 500 7 #define MAXE 40000 8 #define INF 0x7fffffff 9 long ne,nv,tmp,s,t,index; 10 struct Edge{ 11 long next,pair; 12 long v,cap,flow; 13 }edge[MAXE]; 14 long net[MAXN]; 15 long ISAP() 16 { 17 long numb[MAXN],dist[MAXN],curedge[MAXN],pre[MAXN]; 18 long cur_flow,max_flow,u,tmp,neck,i; 19 memset(dist,0,sizeof(dist)); 20 memset(numb,0,sizeof(numb)); 21 memset(pre,-1,sizeof(pre)); 22 for(i = 1 ; i <= nv ; ++i) 23 curedge[i] = net[i]; 24 numb[nv] = nv; 25 max_flow = 0; 26 u = s; 27 while(dist[s] < nv) 28 { 29 /* first , check if has augmemt flow */ 30 if(u == t) 31 { 32 cur_flow = INF; 33 for(i = s; i != t;i = edge[curedge[i]].v) 34 { 35 if(cur_flow > edge[curedge[i]].cap) 36 { 37 neck = i; 38 cur_flow = edge[curedge[i]].cap; 39 } 40 } 41 for(i = s; i != t; i = edge[curedge[i]].v) 42 { 43 tmp = curedge[i]; 44 edge[tmp].cap -= cur_flow; 45 edge[tmp].flow += cur_flow; 46 tmp = edge[tmp].pair; 47 edge[tmp].cap += cur_flow; 48 edge[tmp].flow -= cur_flow; 49 } 50 max_flow += cur_flow; 51 u = neck; 52 } 53 /* if .... else ... */ 54 for(i = curedge[u]; i != -1; i = edge[i].next) 55 if(edge[i].cap > 0 && dist[u] == dist[edge[i].v]+1) 56 break; 57 if(i != -1) 58 { 59 curedge[u] = i; 60 pre[edge[i].v] = u; 61 u = edge[i].v; 62 }else{ 63 if(0 == --numb[dist[u]]) break; 64 curedge[u] = net[u]; 65 for(tmp = nv,i = net[u]; i != -1; i = edge[i].next) 66 if(edge[i].cap > 0) 67 tmp = tmp<dist[edge[i].v]?tmp:dist[edge[i].v]; 68 dist[u] = tmp + 1; 69 ++numb[dist[u]]; 70 if(u != s) u = pre[u]; 71 } 72 } 73 74 return max_flow; 75 } 76 int main() { 77 long i,j,np,nc,m,n; 78 long a,b,val; 79 long g[MAXN][MAXN]; 80 while(scanf("%d%d",&ne,&nv)!=EOF) 81 { 82 s = 1; 83 t = nv; 84 memset(g,0,sizeof(g)); 85 memset(net,-1,sizeof(net)); 86 for(i=0;i<ne;++i) 87 { 88 scanf("%ld%ld%ld",&a,&b,&val); 89 g[a][b] += val; 90 } 91 for(i=1;i<=nv;++i) 92 for(j = i; j <= nv;++j) 93 if(g[i][j]||g[j][i]) 94 { 95 edge[index].next = net[i]; 96 edge[index].v = j; 97 edge[index].cap = g[i][j]; 98 edge[index].flow = 0; 99 edge[index].pair = index+1; 100 net[i] = index++; 101 edge[index].next = net[j]; 102 edge[index].v = i; 103 edge[index].cap = g[j][i]; 104 edge[index].flow = 0; 105 edge[index].pair = index-1; 106 net[j] = index++; 107 } 108 printf("%ld\n",ISAP()); 109 } 110 return 0; 111 }
人生就像心电图,想要一帆风顺,除非game-over