在Acm竞赛中,网络流中求最大流的主流算法有Dinic和Isap,那么这两种算法究竟选哪种好,有些人说Dinic稳定,有些人说Isap效率高,还有人说卡Dinic的题目都是不人道的。为此我分别测试了一下他们的效率。
测试的题目是POJ3469,题目的测试数据足以分辨这两个算法的效率了,所提交的语言为G++。
下面是Dinic的,没有加当前弧优化,用时5469ms。
1 //STATUS:G++_AC_5469MS_8456KB 2 #include<stdio.h> 3 #include<stdlib.h> 4 #include<string.h> 5 #include<math.h> 6 #include<iostream> 7 #include<string> 8 #include<algorithm> 9 #include<vector> 10 #include<queue> 11 #include<stack> 12 #include<map> 13 using namespace std; 14 #define LL long long 15 #define Max(a,b) ((a)>(b)?(a):(b)) 16 #define Min(a,b) ((a)<(b)?(a):(b)) 17 #define mem(a,b) memset(a,b,sizeof(a)) 18 #define lson l,mid,rt<<1 19 #define rson mid+1,r,rt<<1|1 20 const int MAX=20010,INF=0x3f3f3f3f; 21 22 struct Edge{ 23 int u,v,cap; 24 }e[MAX*30]; 25 26 int first[MAX],next[MAX*30],d[MAX]; 27 int n,m,s,t,mm; 28 29 void adde1(int a,int b,int val) 30 { 31 e[mm].u=a;e[mm].v=b; 32 e[mm].cap=val; 33 next[mm]=first[a];first[a]=mm++; 34 e[mm].u=b;e[mm].v=a; 35 e[mm].cap=0; 36 next[mm]=first[b];first[b]=mm++; 37 } 38 39 void adde2(int a,int b,int val) 40 { 41 e[mm].u=a;e[mm].v=b; 42 e[mm].cap=val; 43 next[mm]=first[a];first[a]=mm++; 44 e[mm].u=b;e[mm].v=a; 45 e[mm].cap=val; 46 next[mm]=first[b];first[b]=mm++; 47 } 48 49 int bfs() 50 { 51 int x,i,j; 52 queue<int> q; 53 mem(d,0); 54 q.push(s); 55 d[s]=1; 56 while(!q.empty()){ 57 x=q.front();q.pop(); 58 for(i=first[x];i!=-1;i=next[i]){ 59 if(e[i].cap && !d[e[i].v]){ 60 d[e[i].v]=d[x]+1; 61 q.push(e[i].v); 62 } 63 } 64 } 65 return d[t]; 66 } 67 68 int dfs(int x,int a) 69 { 70 if(x==t || a==0)return a; 71 int f,flow=0; 72 for(int i=first[x];i!=-1;i=next[i]){ 73 if(d[x]+1==d[e[i].v] && (f=dfs(e[i].v,Min(a,e[i].cap)))){ 74 e[i].cap-=f; 75 e[i^1].cap+=f; 76 flow+=f; 77 a-=f; 78 if(!a)break; 79 } 80 } 81 return flow; 82 } 83 84 int dinic() 85 { 86 int i,flow=0; 87 while(bfs()){ 88 flow+=dfs(s,INF); 89 } 90 return flow; 91 } 92 93 int main() 94 { 95 // freopen("in.txt","r",stdin); 96 int i,a,b,val; 97 while(~scanf("%d%d",&n,&m)) 98 { 99 mm=0; 100 mem(first,-1); 101 s=0,t=n+1; 102 for(i=1;i<=n;i++){ 103 scanf("%d%d",&a,&b); 104 adde1(s,i,a); 105 adde1(i,t,b); 106 } 107 for(i=0;i<m;i++){ 108 scanf("%d%d%d",&a,&b,&val); 109 adde2(a,b,val); 110 } 111 112 printf("%d\n",dinic()); 113 } 114 return 0; 115 }
然后是加了当前弧优化的Dinic,用时3891ms。
1 //STATUS:G++_AC_3891MS_8528KB 2 #include<stdio.h> 3 #include<stdlib.h> 4 #include<string.h> 5 #include<math.h> 6 #include<iostream> 7 #include<string> 8 #include<algorithm> 9 #include<vector> 10 #include<queue> 11 #include<stack> 12 #include<map> 13 using namespace std; 14 #define LL long long 15 #define Max(a,b) ((a)>(b)?(a):(b)) 16 #define Min(a,b) ((a)<(b)?(a):(b)) 17 #define mem(a,b) memset(a,b,sizeof(a)) 18 #define lson l,mid,rt<<1 19 #define rson mid+1,r,rt<<1|1 20 const int MAX=20010,INF=0x3f3f3f3f; 21 22 struct Edge{ 23 int u,v,cap; 24 }e[MAX*30]; 25 26 int first[MAX],next[MAX*30],d[MAX],cur[MAX]; 27 int n,m,s,t,mm; 28 29 void adde1(int a,int b,int val) 30 { 31 e[mm].u=a;e[mm].v=b; 32 e[mm].cap=val; 33 next[mm]=first[a];first[a]=mm++; 34 e[mm].u=b;e[mm].v=a; 35 e[mm].cap=0; 36 next[mm]=first[b];first[b]=mm++; 37 } 38 39 void adde2(int a,int b,int val) 40 { 41 e[mm].u=a;e[mm].v=b; 42 e[mm].cap=val; 43 next[mm]=first[a];first[a]=mm++; 44 e[mm].u=b;e[mm].v=a; 45 e[mm].cap=val; 46 next[mm]=first[b];first[b]=mm++; 47 } 48 49 int bfs() 50 { 51 int x,i,j; 52 queue<int> q; 53 mem(d,0); 54 q.push(s); 55 d[s]=1; 56 while(!q.empty()){ 57 x=q.front();q.pop(); 58 for(i=first[x];i!=-1;i=next[i]){ 59 if(e[i].cap && !d[e[i].v]){ 60 d[e[i].v]=d[x]+1; 61 q.push(e[i].v); 62 } 63 } 64 } 65 return d[t]; 66 } 67 68 int dfs(int x,int a) 69 { 70 if(x==t || a==0)return a; 71 int f,flow=0; 72 for(int& i=cur[x];i!=-1;i=next[i]){ 73 if(d[x]+1==d[e[i].v] && (f=dfs(e[i].v,Min(a,e[i].cap)))){ 74 e[i].cap-=f; 75 e[i^1].cap+=f; 76 flow+=f; 77 a-=f; 78 if(!a)break; 79 } 80 } 81 return flow; 82 } 83 84 int dinic() 85 { 86 int i,flow=0; 87 while(bfs()){ 88 for(i=0;i<=t;i++)cur[i]=first[i]; 89 flow+=dfs(s,INF); 90 } 91 return flow; 92 } 93 94 int main() 95 { 96 // freopen("in.txt","r",stdin); 97 int i,a,b,val; 98 while(~scanf("%d%d",&n,&m)) 99 { 100 mm=0; 101 mem(first,-1); 102 s=0,t=n+1; 103 for(i=1;i<=n;i++){ 104 scanf("%d%d",&a,&b); 105 adde1(s,i,a); 106 adde1(i,t,b); 107 } 108 for(i=0;i<m;i++){ 109 scanf("%d%d%d",&a,&b,&val); 110 adde2(a,b,val); 111 } 112 113 printf("%d\n",dinic()); 114 } 115 return 0; 116 }
这道题的时限是15000ms,从上面可以看出,一般来说,Dinic已经完全够用了,而且Dinic的代码量不大,敲起来速度快,逻辑结构清晰,所以Dinic在比赛中还是挺好用的。更要注意的是,当前弧优化是一定要加的,上面加了速度快了不少,而且好像有些题目就是专门卡这个,加了就能轻松AC,不加直接TLE!
然后来看看Isap的效率。
开始没有初始化距离数组d[]的Isap,用时2391ms。
1 //STATUS:G++_AC_2391MS_8592KB 2 #include<stdio.h> 3 #include<stdlib.h> 4 #include<string.h> 5 #include<math.h> 6 #include<iostream> 7 #include<string> 8 #include<algorithm> 9 #include<vector> 10 #include<queue> 11 #include<stack> 12 #include<map> 13 using namespace std; 14 #define LL long long 15 #define Max(a,b) ((a)>(b)?(a):(b)) 16 #define Min(a,b) ((a)<(b)?(a):(b)) 17 #define mem(a,b) memset(a,b,sizeof(a)) 18 #define lson l,mid,rt<<1 19 #define rson mid+1,r,rt<<1|1 20 const int MAX=20010,INF=0x3f3f3f3f; 21 22 struct Edge{ 23 int u,v,cap; 24 }e[MAX*30]; 25 26 int first[MAX],next[MAX*30],d[MAX],cur[MAX],fa[MAX],num[MAX]; 27 int n,m,s,t,mm; 28 29 void adde1(int a,int b,int val) 30 { 31 e[mm].u=a;e[mm].v=b; 32 e[mm].cap=val; 33 next[mm]=first[a];first[a]=mm++; 34 e[mm].u=b;e[mm].v=a; 35 e[mm].cap=0; 36 next[mm]=first[b];first[b]=mm++; 37 } 38 39 void adde2(int a,int b,int val) 40 { 41 e[mm].u=a;e[mm].v=b; 42 e[mm].cap=val; 43 next[mm]=first[a];first[a]=mm++; 44 e[mm].u=b;e[mm].v=a; 45 e[mm].cap=val; 46 next[mm]=first[b];first[b]=mm++; 47 } 48 49 int augment() 50 { 51 int x=t,a=INF; 52 while(x!=s){ 53 a=Min(a,e[fa[x]].cap); 54 x=e[fa[x]].u; 55 } 56 x=t; 57 while(x!=s){ 58 e[fa[x]].cap-=a; 59 e[fa[x]^1].cap+=a; 60 x=e[fa[x]].u; 61 } 62 return a; 63 } 64 65 int isap() 66 { 67 int i,x,ok,min,flow=0; 68 mem(d,0);mem(num,0); 69 num[0]=n; 70 for(i=0;i<n;i++)cur[i]=first[i]; 71 x=s; 72 while(d[s]<n){ 73 if(x==t){ 74 flow+=augment(); 75 x=s; 76 } 77 ok=0; 78 for(i=cur[x];i!=-1;i=next[i]){ 79 if(e[i].cap && d[x]==d[e[i].v]+1){ 80 ok=1; 81 fa[e[i].v]=i; 82 cur[x]=i; 83 x=e[i].v; 84 break; 85 } 86 } 87 if(!ok){ 88 min=n-1; 89 for(i=first[x];i!=-1;i=next[i]) 90 if(e[i].cap && d[e[i].v]<min)min=d[e[i].v]; 91 if(--num[d[x]]==0)break; 92 num[d[x]=min+1]++; 93 cur[x]=first[x]; 94 if(x!=s)x=e[fa[x]].u; 95 } 96 } 97 return flow; 98 } 99 100 int main() 101 { 102 // freopen("in.txt","r",stdin); 103 int i,a,b,val; 104 while(~scanf("%d%d",&n,&m)) 105 { 106 mm=0; 107 mem(first,-1); 108 s=0,t=n+1; 109 for(i=1;i<=n;i++){ 110 scanf("%d%d",&a,&b); 111 adde1(s,i,a); 112 adde1(i,t,b); 113 } 114 for(i=0;i<m;i++){ 115 scanf("%d%d%d",&a,&b,&val); 116 adde2(a,b,val); 117 } 118 119 n+=2; 120 printf("%d\n",isap()); 121 } 122 return 0; 123 }
用bfs初始化距离数组d[]的Isap,用时2454ms。
1 //STATUS:G++_AC_2454MS_8684KB 2 #include<stdio.h> 3 #include<stdlib.h> 4 #include<string.h> 5 #include<math.h> 6 #include<iostream> 7 #include<string> 8 #include<algorithm> 9 #include<vector> 10 #include<queue> 11 #include<stack> 12 #include<map> 13 using namespace std; 14 #define LL long long 15 #define Max(a,b) ((a)>(b)?(a):(b)) 16 #define Min(a,b) ((a)<(b)?(a):(b)) 17 #define mem(a,b) memset(a,b,sizeof(a)) 18 #define lson l,mid,rt<<1 19 #define rson mid+1,r,rt<<1|1 20 const int MAX=20010,INF=0x3f3f3f3f; 21 22 struct Edge{ 23 int u,v,cap; 24 }e[MAX*30]; 25 26 int first[MAX],next[MAX*30],d[MAX],cur[MAX],fa[MAX],num[MAX]; 27 int n,m,s,t,mm; 28 29 void adde1(int a,int b,int val) 30 { 31 e[mm].u=a;e[mm].v=b; 32 e[mm].cap=val; 33 next[mm]=first[a];first[a]=mm++; 34 e[mm].u=b;e[mm].v=a; 35 e[mm].cap=0; 36 next[mm]=first[b];first[b]=mm++; 37 } 38 39 void adde2(int a,int b,int val) 40 { 41 e[mm].u=a;e[mm].v=b; 42 e[mm].cap=val; 43 next[mm]=first[a];first[a]=mm++; 44 e[mm].u=b;e[mm].v=a; 45 e[mm].cap=val; 46 next[mm]=first[b];first[b]=mm++; 47 } 48 49 void bfs() 50 { 51 int x,i,j; 52 queue<int> q; 53 mem(d,-1); 54 q.push(t); 55 d[t]=0; 56 while(!q.empty()){ 57 x=q.front();q.pop(); 58 for(i=first[x];i!=-1;i=next[i]){ 59 if(d[e[i].v]<0){ 60 d[e[i].v]=d[x]+1; 61 q.push(e[i].v); 62 } 63 } 64 } 65 } 66 67 int augment() 68 { 69 int x=t,a=INF; 70 while(x!=s){ 71 a=Min(a,e[fa[x]].cap); 72 x=e[fa[x]].u; 73 } 74 x=t; 75 while(x!=s){ 76 e[fa[x]].cap-=a; 77 e[fa[x]^1].cap+=a; 78 x=e[fa[x]].u; 79 } 80 return a; 81 } 82 83 int isap() 84 { 85 int i,x,ok,min,flow=0; 86 mem(num,0); 87 bfs(); 88 for(i=0;i<n;i++)num[d[i]]++; 89 for(i=0;i<n;i++)cur[i]=first[i]; 90 x=s; 91 while(d[s]<n){ 92 if(x==t){ 93 flow+=augment(); 94 x=s; 95 } 96 ok=0; 97 for(i=cur[x];i!=-1;i=next[i]){ 98 if(e[i].cap && d[x]==d[e[i].v]+1){ 99 ok=1; 100 fa[e[i].v]=i; 101 cur[x]=i; 102 x=e[i].v; 103 break; 104 } 105 } 106 if(!ok){ 107 min=n-1; 108 for(i=first[x];i!=-1;i=next[i]) 109 if(e[i].cap && d[e[i].v]<min)min=d[e[i].v]; 110 if(--num[d[x]]==0)break; 111 num[d[x]=min+1]++; 112 cur[x]=first[x]; 113 if(x!=s)x=e[fa[x]].u; 114 } 115 } 116 return flow; 117 } 118 119 int main() 120 { 121 // freopen("in.txt","r",stdin); 122 int i,a,b,val; 123 while(~scanf("%d%d",&n,&m)) 124 { 125 mm=0; 126 mem(first,-1); 127 s=0,t=n+1; 128 for(i=1;i<=n;i++){ 129 scanf("%d%d",&a,&b); 130 adde1(s,i,a); 131 adde1(i,t,b); 132 } 133 for(i=0;i<m;i++){ 134 scanf("%d%d%d",&a,&b,&val); 135 adde2(a,b,val); 136 } 137 138 n+=2; 139 printf("%d\n",isap()); 140 } 141 return 0; 142 }
发现Isap的效率比Dinic的效率是快了一倍样子!果然当前弧优化+距离标号的算法效率很高,而且没有初始化距离数组的d[]的Isap代码也很短,性价比非常高。至于初始化了距离数组d[]的Isap虽然最求一次最大流效率没怎么改变,但是如果是多次求解规模较小的网络流是,效率还是会有明显提升的。
其实我觉得Dinic无论从那个方面来讲,都是劣于Isap的,从时间效率上来讲,Isap快于Dinic,从代码上来讲,Isap和Dnic还是差不了多少,甚至我觉得Isap写起来还要短些,而且更为重要的是Isap可以直接一个循环结构搞定,而Dinic的递归写起来虽然方便,但某些时候可能会遇到爆栈的情况,如果写成迭代形式的Dinic就还需要维护一个栈,相对起来又麻烦些。所以,我们没有理由不用Isap,至于有人说Dinic更稳定,以后有待验证!