算法设计 最大网络流

算法设计 最大网络流

最大网络流问题是图论中的经典问题,以前只是学习了Ford-Fulkerson方法,趁现在
比较“轻闲”把网络流的Push-relabel方法也学习一下。我们知道利用最短路径的Ford-Fulkerson方法实现算法,其
时间复杂度可以达到O(VE^2),而Push-relabel方法的算法实现(relabel-to-front)的复杂度可以达到O(V^3).

先说明一下基本概念:
c(u,v)代表网络管道(u,v)之间的流量限制,其值总是大于零的(c(u,v)=0 <=>no edge exist between u and v)
f(u,v)代表网络管道(u,v)之间的实际流量,当然不能超过c(u,v)的限制,且f(u,v)=-f(v,u)。
residual网络代表所有的剩余流组成的网络且该网络上的限制cf(u,v)=c(u,v)-f(u,v)(c,f分别是原网络的参数)
该网络可以表示为G = (V,Ef)。

Ford-Fulkerson方法的思想是每次都找到一个增广路径,然后填满该增广路径上的所有瓶颈流量。不断找到增广路径直到
没有为止,这样就可以找到一种最大流方案。该方法是从全局考虑每个路径的能力,让每一个路径都尽力而为,而Push-relabel
方法则将流量调整的权利下放到每一个节点,通过节点讲多余的流量挤出网络从而是网络的流量达到最大。那么怎么样某
一个节点能够讲他多余出来的流挤到下一个节点呢,就是该节点的势比较高,就是高度比较高,这里面有一个
Admissible edge((u,v)属于Ef并且h(u)=h(v)+1(h就是高度))的概念,就是能够通过这个edge将多余流量挤出去的管道。
通过反复的挤出流最终达到流量的最大。具体参考(算法导论),导论中先讲了Push Relabel 方法,然后介绍了一个基于该方法
的relabel-to-front算法。期中discharge方法相当于将自己多余的流挤给其他人,而总的relabel-to-front方法就是不停的调整
直到没有人可以将自己多余的流挤个其他人为止,这里在push的时候会因为管道能力的问题将多余流量扔掉,最后到达T的时候的
流量就是最后的最大流。

下面是relabel-to-front算法的具体实现
注意要使用g++编译器,vc需要将变量声明修改一下
  1. #include <iostream>
  2. #include <climits>
  3. using namespace std;
  4. #define BUF 100
  5. int f[BUF][BUF];
  6. int c[BUF][BUF];
  7. struct Edge{
  8.     int v;
  9.     Edge *next;
  10. };
  11. struct Node{
  12.     int h;
  13.     int ef;
  14.     Edge *firste;
  15. };
  16.     
  17. Node graph[BUF];
  18. Edge *current[BUF];
  19. struct List{
  20.     int n;
  21.     List *next;
  22. };
  23. List *L;
  24. int N;
  25. int S,T;
  26. /*
  27. sample input:
  28. 6 1 6 10
  29. 1 2 16
  30. 1 4 13
  31. 2 4 10
  32. 4 2 4
  33. 2 3 12
  34. 3 4 9
  35. 4 5 14
  36. 5 3 7
  37. 3 6 20
  38. 5 6 4
  39. the first line represent |V|=6,S=1,T=6,|E|=10.
  40. then the next ten lines represent 10 edges of the graph.
  41. */
  42. void init_graph()
  43. {
  44.     int eN;
  45.     
  46.     scanf("%d%d%d%d",&N,&S,&T,&eN);
  47.     for(int i=1;i<N;i++)
  48.         graph[i].firste = NULL;
  49.         
  50.     for(int i=1;i<=eN;i++){
  51.         
  52.         int s,e;
  53.         
  54.         scanf("%d%d",&s,&e);
  55.         
  56.         if(c[e][s]==0){
  57.             Edge *e1 = new Edge();
  58.             Edge *e2 = new Edge();
  59.         
  60.             e1->v = s,e2->v = e;
  61.         
  62.             e1->next = graph[e].firste;
  63.             e2->next = graph[s].firste;
  64.             
  65.             graph[e].firste = e1;
  66.             graph[s].firste = e2;
  67.         }
  68.         
  69.         scanf("%d",&c[s][e]);
  70.     }
  71. }
  72. //to print the debug information 
  73. void print()
  74. {
  75.     for(int i=1;i<=N;i++){
  76.         for(int j=1;j<=N;j++)
  77.             printf("%d ",c[i][j]);      
  78.         printf("/n");
  79.     }
  80.     printf("/n");
  81.     for(int i=1;i<=N;i++){
  82.         for(int j=1;j<=N;j++)
  83.             printf("%d ",f[i][j]);      
  84.         printf("/n");
  85.     }
  86.     printf("/n");
  87.     List *p = L->next;
  88.     while(p!=NULL){
  89.         printf("%d ",p->n);
  90.         p = p->next;
  91.     }
  92.     printf("/n");
  93.     for(int i=1;i<=N;i++)
  94.         printf("%d ",graph[i].ef);
  95.     printf("/n/n");
  96. }
  97. void init_preflow()
  98. {
  99.     for(int i=0;i<N;i++){
  100.         graph[i].h = 0;
  101.         graph[i].ef = 0;
  102.     }
  103.     
  104.     fill_n(&f[0][0],BUF*BUF,0);
  105.     
  106.     graph[S].h = N;
  107.     
  108.     for(Edge *p=graph[S].firste;p!=NULL;p=p->next){ 
  109.         f[S][p->v] = c[S][p->v];
  110.         f[p->v][S] = -1*c[S][p->v];
  111.         graph[p->v].ef = c[S][p->v];
  112.         graph[S].ef -= c[S][p->v];
  113.     }
  114. }
  115. void init_list()
  116. {
  117.     L = new List();
  118.     List *p = L;
  119.     
  120.     for(int i=1;i<=N;i++)
  121.         if(i!=S&&i!=T){
  122.             p->next = new List();
  123.             p->next->n = i;
  124.             p = p->next;
  125.         }
  126.     p->next = NULL;
  127. }
  128.             
  129. // the next four functions are the core of the relabel to front algorithm.
  130.     
  131. void push(int u,int v)
  132. {
  133.     if(graph[u].ef>0&&graph[u].h==(graph[v].h+1)){
  134.         int df = c[u][v]-f[u][v];
  135.         df = min(df,graph[u].ef);
  136.         f[u][v] += df;
  137.         f[v][u] = -1*f[u][v];
  138.         graph[u].ef -= df;
  139.         graph[v].ef += df;
  140.     }
  141. }
  142. void relabel(int u)
  143. {
  144.     if(graph[u].ef>0){
  145.         
  146.         int low = INT_MAX;
  147.         bool have = false;
  148.         
  149.         for(Edge *p=graph[u].firste;p!=NULL;p=p->next)
  150.             if(c[u][p->v]-f[u][p->v]>0&&graph[u].h<=graph[p->v].h){                 
  151.                 low = min(low,graph[p->v].h);
  152.                 have = true;
  153.             }
  154.         if(have)
  155.             graph[u].h = low+1;
  156.     }
  157. }
  158.             
  159. void discharge(int u)
  160. {
  161.     while(graph[u].ef>0){
  162.         
  163.         Edge *pv = current[u];
  164.         
  165.         if(pv==NULL){
  166.             relabel(u);
  167.             current[u] = graph[u].firste;
  168.         }else if(c[u][pv->v]-f[u][pv->v]>0&&graph[u].h==(graph[pv->v].h+1)){
  169.             push(u,pv->v);
  170.         }else {
  171.             current[u] = pv->next;
  172.         }
  173.     }
  174. }
  175.         
  176. void relabel_front()
  177. {
  178.     init_preflow();
  179.     
  180.     init_list();
  181.     
  182.     for(int i=1;i<=N;i++)
  183.         if(i!=S&&i!=T)
  184.             current[i] = graph[i].firste;
  185.     
  186.     List *q = L;
  187.     List *p = L->next;
  188.     
  189.     while(p!=NULL){
  190.         
  191.         int oh = graph[p->n].h;
  192.         discharge(p->n);
  193.         
  194.         if(graph[p->n].h>oh){
  195.             
  196.             q->next = p->next;
  197.             q = L;
  198.             p->next = q->next;
  199.             q->next = p;
  200.         }
  201.         
  202.         p = p->next;
  203.         q = q->next;
  204. //      print();
  205.     }
  206. }
  207.             
  208. int main()
  209. {
  210.     freopen("rf1.in","r",stdin);
  211.     freopen("rf.out","w",stdout);
  212.     
  213.     init_graph();
  214.     
  215.     relabel_front();
  216.     
  217.     printf("%d/n",-1*graph[S].ef);
  218.     
  219.     return 0;
  220. }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值