hdu  4289  control   

 http://acm.hdu.edu.cn/showproblem.php?pid=4289

 

我自己的代码:

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<cmath>
  4 #include<iostream>
  5 #include<algorithm>
  6 #include<set>
  7 #include<map>
  8 #include<queue>
  9 #include<vector>
 10 #include<string>
 11 #define INF 0x7fffffff
 12 #define maxn 500
 13 #define CL(a,b) memset(a,b,sizeof(a))
 14 
 15 using namespace std;
 16 struct node
 17 {
 18     int to;
 19     int cap;
 20     int  next;
 21 }p[200000] ;
 22 int dis[maxn],gap[maxn] ,cnt,next[maxn],s,e;// dis[i]为 到达 原点的层数
 23 int  n , m,NN ;//NN 为  加完点 之后的 总结点数
 24 void add(int from,int to,int cap)//加 的 是 有向边
 25 {
 26     p[cnt].to = to;
 27     p[cnt].cap = cap ;
 28     p[cnt].next = next[from];
 29     next[from] = cnt++;
 30 
 31     p[cnt].to = from;
 32     p[cnt].cap = 0;
 33     p[cnt].next = next[to];
 34     next[to] = cnt++ ;
 35 }
 36 int dfs(int pos,int cost)
 37 {
 38 
 39     if(pos == e)
 40      return cost ;
 41 
 42     int i,j ,mdis = NN ,f = cost ;
 43 
 44     for(i =  next[pos];i != - 1; i = p[i].next)
 45     {
 46         int to = p[i].to ;
 47         int cap = p[i].cap ;
 48         if(cap > 0 )
 49         {
 50             if(dis[to] + 1 == dis[pos])
 51             {
 52 
 53 
 54               int d = min(f,cap) ;// 注意 这 为 剩余 流量 和 cap 的 最小值
 55 
 56               d = dfs(to,d) ;
 57               p[i].cap -=d;
 58               p[i^1].cap +=d;
 59               f -= d;
 60 
 61               if(dis[s] >= NN)  return cost - f;// 如果没有 了 增广路经 结束算法
 62               if(f == 0break ;
 63             }
 64             if( dis[to] < mdis ) mdis = dis[to] ;// 记录可扩展的最小的狐
 65 
 66         }
 67 
 68     }
 69     if(f == cost)//  没有 可以 扩展的点
 70     {
 71         --gap[dis[pos]];
 72         if(gap[dis[pos]] == 0)dis[s] = NN;// 注意这 ,若 距离 为 dis[pos] 这一层都没有 扩展点了(断层) dis[s] = n
 73 
 74         dis[pos] = mdis + 1;//维护距离标号的方法是这样的:当找增广路过程中发现某点出发没有允许弧时,将这个点的距离标号设为由它出发的所有弧的终点的距离标号的最                                     小值加一
 75 
 76         ++gap[dis[pos]] ;
 77     }
 78     return cost  - f ;
 79 }
 80 int isap( int b,int t)
 81 {
 82 
 83     int ret =  0;
 84     s = b;
 85     e = t;
 86     CL(gap,0);
 87     CL(dis,0) ;
 88     gap[s] = NN ;//NN 为  加完点 之后的 总结点数
 89     while(dis[s] < NN)
 90     {
 91         ret+=dfs(s,INF) ;
 92     }
 93     return ret ;
 94 
 95 }
 96 int main()
 97 {
 98     int  i,x,y,a,b,t;
 99     //freopen("data.txt","r",stdin) ;
100     while(scanf("%d%d%d%d",&n,&m,&b,&t)!=EOF)
101     {
102         CL(next,-1);
103         cnt = 0 ;
104         NN = 2*n  ;//NN 为  加完点 之后的 总结点数
105         for(i = 1;i <=n;i++)
106         {
107             scanf("%d",&a);
108             add(i,i+n,a) ;
109         }
110         for(i = 0; i<m;i++)
111         {
112             scanf("%d%d",&x,&y);
113             add(x+n,y,INF);
114             add(y + n,x,INF);
115         }
116 
117         int ans = isap(b,t + n) ;
118         printf("%d\n",ans) ;
119     }
120 }

 

讲解:

http://hi.baidu.com/jhubtjkpmbfpqzr/item/9223a400c14418dbdce5b027 

另一 讲解:

原文 from Lost 庄神

原来我的模板是这么来的。至今思网络,不知怎么流。惭愧啊……

ISAP全称Improved Shortest Augmenting Path,由Ahuja和Orlin在1987年提出,而下文讲的是加上gap优化的ISAP。

顺便,其实这篇写得比较入门和清楚。

====

  这几天由于种种原因经常接触到网络流的题目,这一类型的题给人的感觉,就是要非常使劲的YY才能出来点比较正常的模型。尤其是看了Amber最 小割应用的文章,里面的题目思路真是充满了绵绵不绝的YD思想。然而比赛中,当你YD到了这一层后,您不得不花比较多的时间去纠结于大量细节的实现,而冗 长的代码难免会使敲错版后的调试显得异常悲伤,因此一些巧妙简短高效的网络流算法在此时便显得犹为重要了。本文力求以最简短的描述,对比较流行的网络流算 法作一定的总结,并借之向读者强烈推荐一种效率与编程复杂度相适应的算法。

  众所周知,在网络流的世界里,存在2类截然不同的求解思想,就是比较著名的预流推进与增广路,两者都需要反向边的小技巧。

  其中预流推进的算法思想是以边为单元进行推流操作。具体流程如下:置初始点邻接边满流并用一次反向bfs对每个结点计算反向距离标号,定义除汇 点外存量大于出量的结点为活动结点,每次对活动结点按允许边(u->v:d[u]=d[v]+1)进行推流操作,直到无法推流或者该点存量为0,若 u点此时仍为活动结点,则进行重标号,使之等于原图中进行推操作后的邻接结点的最小标号+1,并将u点入队。当队列为空时,算法结束,只有s点和t点存量 非0,网络中各顶点无存量,无法找到增广路继续增广,则t点存量为最大流。

  而增广路的思想在于每次从源点搜索出一条前往汇点的增广路,并改变路上的边权,直到无法再进行增广,此时汇点的增广量即为最大流。两者最后的理 论基础依然是增广路定理,而在理论复杂度上预流推进要显得比较优秀。其中的HLPP高标预流推进的理论复杂度已经达到了另人发指的 O(sqrt(m)*n*n),但是其编程复杂度也是同样的令人发指- -

  于是我们能否在编程复杂度和算法复杂度上找到一个平衡呢,答案是肯定的。我们使用增广路的思想,而且必须进行优化。因为原始的增广路算法(例如 EK)是非常悲剧的。于是有人注意到了预流推进中的标号法,在增广路算法中引入允许弧概念,每次反搜残留网络得到结点标号,在正向增广中利用递归进行连续 增广,于是产生了基于分层图的Dinic算法。一些人更不满足于常规Dinic所带来的提升,进而加入了多路分流增广的概念,即对同一顶点的流量,分多路 同时推进,再加上比较复杂的手工递归,使得Dinic已经满足大部分题目的需要。

  然而这样做就是增广路算法优化的极限么?答案永远是不。人们在Dinic中只类比了预流推进的标号技术,而重标号操作却没有发挥得淋漓尽致。于 是人们在Dinic的基础上重新引入了重标号的概念,使得算法无须在每次增广后再进行BFS每个顶点进行距离标号,这种主动标号技术使得修正后算法的速度 有了不少提高。但这点提高是不足称道的,人们又发现当某个标号的值没有对应的顶点后,即增广路被截断了,于是算法便可以提前结束,这种启发式的优化称为 Gap优化。最后人们结合了连续增广,分层图,多路增广,Gap优化,主动标号等穷凶极恶的优化,更甚者在此之上狂搞个手动递归,于是产生了增广路算法的 高效算法–ISAP算法。

  虽然ISAP算法的理论复杂度仍然不可超越高标预流推进,但其编程复杂度已经简化到发指,如此优化,加上不逊于Dinic的速率(在效率上手工Dinic有时甚至不如递归ISAP),我们没有不选择它的理由。

  因此本文强烈推荐ISAP作为网络流首选算法。

  其实现方法见下文,除去例行的添边操作,不超过50行的代码,何乐而不为之,以下实现仍有优化的余地(在计算初始标号时,为减小代码量直接忽略之,其复杂度不变,但实现后效率有5%左右的下降,如果乐意修正的话可以进行改良,当然不修正不影响算法正确性)。

  1 typedef  struct {int v,next,val;} edge;
  2 const int MAXN=20010;
  3 const int MAXM=500010;
  4  
  5 edge e[MAXM];
  6 int p[MAXN],eid;
  7  
  8 inline void init(){memset(p,-1,sizeof(p));eid=0;}
  9  
 10 //有向
 11 inline void insert1(int from,int to,int val)
 12 {
 13     e[eid].v=to;
 14     e[eid].val=val;
 15     e[eid].next=p[from];
 16     p[from]=eid++;
 17  
 18     swap(from,to);
 19  
 20     e[eid].v=to;
 21     e[eid].val=0;
 22     e[eid].next=p[from];
 23     p[from]=eid++;
 24 }
 25  
 26 //无向
 27 inline void insert2(int from,int to,int val)
 28 {
 29     e[eid].v=to;
 30     e[eid].val=val;
 31     e[eid].next=p[from];
 32     p[from]=eid++;
 33  
 34     swap(from,to);
 35  
 36     e[eid].v=to;
 37     e[eid].val=val;
 38     e[eid].next=p[from];
 39     p[from]=eid++;
 40 }
 41  
 42 int n,m;//n为点数 m为边数
 43 int h[MAXN];
 44 int gap[MAXN];
 45  
 46 int source,sink;
 47 inline int dfs(int pos,int cost)
 48 {
 49     if (pos==sink)
 50     {
 51         return cost;
 52     }
 53  
 54     int j,minh=n-1,lv=cost,d;
 55  
 56     for (j=p[pos];j!=-1;j=e[j].next)
 57     {
 58         int v=e[j].v,val=e[j].val;
 59         if(val>0)
 60         {
 61             if (h[v]+1==h[pos])
 62             {
 63                 if (lv<e[j].val) d=lv;
 64                 else d=e[j].val;
 65  
 66                 d=dfs(v,d);
 67                 e[j].val-=d;
 68                 e[j^1].val+=d;
 69                 lv-=d;
 70                 if (h[source]>=n) return cost-lv;
 71                 if (lv==0break;
 72             }
 73  
 74             if (h[v]<minh)    minh=h[v];
 75         }
 76     }
 77  
 78     if (lv==cost)
 79     {
 80         --gap[h[pos]];
 81         if (gap[h[pos]]==0) h[source]=n;
 82         h[pos]=minh+1;
 83         ++gap[h[pos]];
 84     }
 85  
 86     return cost-lv;
 87  
 88 }
 89  
 90 int sap(int st,int ed)
 91 {
 92  
 93     source=st;
 94     sink=ed;
 95     int ret=0;
 96     memset(gap,0,sizeof(gap));
 97     memset(h,0,sizeof(h));
 98  
 99     gap[st]=n;
100  
101     while (h[st]<n)
102     {
103         ret+=dfs(st,INT_MAX);
104     }
105  
106     return ret;
107 }

相关文章:

  • 2021-11-18
  • 2021-06-01
  • 2021-09-14
  • 2022-12-23
  • 2021-10-03
  • 2022-12-23
  • 2022-02-27
  • 2022-12-23
猜你喜欢
  • 2021-06-12
  • 2021-05-25
  • 2021-07-14
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-10-23
相关资源
相似解决方案