Dinic PK Isap

  在Acm竞赛中,网络流中求最大流的主流算法有Dinic和Isap,那么这两种算法究竟选哪种好,有些人说Dinic稳定,有些人说Isap效率高,还有人说卡Dinic的题目都是不人道的。为此我分别测试了一下他们的效率。

  测试的题目是POJ3469,题目的测试数据足以分辨这两个算法的效率了,所提交的语言为G++。

  下面是Dinic的,没有加当前弧优化,用时5469ms。

Dinic-没有当前弧优化
  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。

Dinic-有当前弧优化
  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。

Isap-没有初始化d[]
  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。

Isap-初始化了d[]
  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更稳定,以后有待验证!

 

 

 

posted @ 2012-12-03 18:04  zhsl  阅读(2908)  评论(0编辑  收藏  举报