网络流总结以及模版 && POJ3498

增广路方法:

    ①一般增广路算法O(mnU):采取标号法每次在容量网络中寻找一条增广路(或者在残留网络中,每次任意找一条增广路),知道不存在增广路。

模板:

View Code
  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算法:

View Code
  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算法:

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

View Code
 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)):每次检查具有最高标号的活跃结点。

 

posted @ 2012-08-24 20:47  _sunshine  阅读(611)  评论(0编辑  收藏  举报