您的位置:首页 > 理论基础 > 计算机网络

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

2015-09-16 16:34 597 查看
增广路方法:

①一般增广路算法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
,totaledge,totalpoint;
16  int cnt
,d
;
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
[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
;
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
,totaledge,totalpoint;
15  int q
,d
;
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
[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
;
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)):每次检查具有最高标号的活跃结点。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: