[转载 ]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 }
View Code

空间优化: 

在程序实现的时候,我们通常只是用一个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 }
View Code

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  }
View Code

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 }
View Code

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 }
View Code

 

posted @ 2015-09-04 17:19  一麻袋码的玛侬  阅读(283)  评论(0编辑  收藏  举报