您的位置:首页 > 其它

isap 最大流 模版

2012-09-22 09:42 211 查看
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 == 0) break ;
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) ;
while(scanf("%d%d%d%d",&n,&m,&b,&t)!=EOF)
{
CL(next,-1);
cnt = 0 ;
NN = 2*n ;//NN 为 加完点 之后的 总结点数
for(i = 1;i <=n;i++)
{
scanf("%d",&a);
add(i,i+n,a) ;
}
for(i = 0; i<m;i++)
{
scanf("%d%d",&x,&y);
add(x+n,y,INF);
add(y + n,x,INF);
}

int ans = isap(b,t + n) ;
printf("%d\n",ans) ;
}
}

讲解:

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

另一 讲解:

[转载]网络流ISAP算法的简单介绍

原文 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%左右的下降,如果乐意修正的话可以进行改良,当然不修正不影响算法正确性)。

View Code 1 #include <cstdio>
2 #include <cstring>
3 using namespace std;
4 const int V=220,E=220;
5
6 int n,m,h[V],vh[V];
7
8 struct etype
9 {
int t,u;
etype *next,*pair;
etype(){}
etype(int t_,int u_,etype* next_):t(t_),u(u_),next(next_){}
void* operator new(unsigned,void* p){return p;}
} *e[V],*eb[V],Te[E+E],*Pe=Te;

int aug(int no,int m)
{
if(no==n)return m;
int l=m;
for(etype *&i=e[no];i;i=i->next)
if(i->u && h[i->t]+1==h[no])
{
int d=aug(i->t,l<i->u?l:i->u);
i->u-=d,i->pair->u+=d,l-=d;
if(h[1]==n || !l)return m-l;
}
int minh=n;
for(etype *i=e[no]=eb[no];i;i=i->next)if(i->u)
if(h[i->t]+1<minh)minh=h[i->t]+1;
if(!--vh[h[no]])h[1]=n;else ++vh[h[no]=minh];
return m-l;
}

int main()
{
freopen("ditch.in","r",stdin);
freopen("ditch.out","w",stdout);
scanf("%d %d",&m,&n);
memset(e,0,sizeof(e));
while(m--)
{
int s,t,u;
scanf("%d %d %d",&s,&t,&u);
e[s]=new(Pe++)etype(t,u,e[s]);
e[t]=new(Pe++)etype(s,0,e[t]);
e[s]->pair=e[t];
e[t]->pair=e[s];
}
memmove(eb,e,sizeof(e));
memset(h,0,sizeof(h));
memset(vh,0,sizeof(vh));
vh[0]=n;
int ans=0;
while(h[1]<n)ans+=aug(1,~0U>>1);
printf("%d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: