今天刻意用poj 3469 http://poj.org/problem?id=3469测了下模板,Isap并不像想象中那么快,难道是我写搓了,而且在网络流与线性规划中的最后一题,isap完败给Dinic了,我的Isap啊~~~不知道那些几百毫秒出解的是用什么算法。。。难道是。。。 dinic总体上挺不错的,递归版的Isap基本上与Dinic没差别,而非递归版在某些情况反而不如递归版,于是以后一般用Dinic,是在不行用非递归的isap试试 所以比赛中就带1,3两个模板好了 费用流居然忘加了,今天把它加上了= =,在最后。。。 模板1(Dinic递归版 3438MS): |
#include<cstdio> using namespace std; const int mm=1000000; const int mn=22222; const int oo=1000000000; int node,src,dest,edge; int reach[mm],flow[mm],next[mm]; int head[mn],work[mn],dis[mn],q[mn]; inline int min(int a,int b) { return a<b?a:b; } inline void prepare(int _node,int _src,int _dest) { node=_node,src=_src,dest=_dest; for(int i=0;i<node;++i)head[i]=-1; edge=0; } inline void addedge(int u,int v,int c1,int c2) { reach[edge]=v,flow[edge]=c1,next[edge]=head[u],head[u]=edge++; reach[edge]=u,flow[edge]=c2,next[edge]=head[v],head[v]=edge++; } bool Dinic_bfs() { int i,u,v,l,r=0; for(i=0;i<node;++i)dis[i]=-1; dis[q[r++]=src]=0; for(l=0;l<r;++l) for(i=head[u=q[l]];i>=0;i=next[i]) if(flow[i]&&dis[v=reach[i]]<0) { dis[q[r++]=v]=dis[u]+1; if(v==dest)return 1; } return 0; } int Dinic_dfs(int u,int exp) { if(u==dest)return exp; for(int &i=work[u],v,tmp;i>=0;i=next[i]) if(flow[i]&&dis[v=reach[i]]==dis[u]+1&&(tmp=Dinic_dfs(v,min(exp,flow[i])))>0) { flow[i]-=tmp; flow[i^1]+=tmp; return tmp; }dis[u]--; return 0; } int Dinic_flow() { int i,ret=0,delta; while(Dinic_bfs()) { for(i=0;i<node;++i)work[i]=head[i]; while(delta=Dinic_dfs(src,oo))ret+=delta; } return ret; } int main() { int i,j,n,m,a,b; while(scanf("%d%d",&n,&m)!=-1) { prepare(n+2,0,n+1); for(i=1;i<=n;++i) { scanf("%d%d",&a,&b); addedge(src,i,a,0); addedge(i,dest,b,0); } while(m--) { scanf("%d%d%d",&i,&j,&a); addedge(i,j,a,a); } printf("%d\n",Dinic_flow()); } return 0; }
#include<cstdio> using namespace std; const int mm=2000000; const int mn=22222; const int oo=1000000000; int node,src,dest,edge; int reach[mm],flow[mm],next[mm]; int head[mn],gap[mn],h[mn]; inline int min(int a,int b) { return a<b?a:b; } inline void prepare(int _node,int _src,int _dest) { node=_node,src=_src,dest=_dest; for(int i=0;i<node;++i)head[i]=-1; edge=0; } inline void addedge(int u,int v,int c1,int c2) { reach[edge]=v,flow[edge]=c1,next[edge]=head[u],head[u]=edge++; reach[edge]=u,flow[edge]=c2,next[edge]=head[v],head[v]=edge++; } int Isap_dfs(int u,int exp) { if(u==dest)return exp; int i,v,minh=node,lv=exp,tmp; for(i=head[u];i>=0;i=next[i]) if(flow[i]) { if(h[v=reach[i]]+1==h[u]) { tmp=Isap_dfs(v,min(lv,flow[i])); flow[i]-=tmp; flow[i^1]+=tmp; lv-=tmp; if(h[src]>=node)return exp-lv; if(lv==0)break; } minh=min(minh,h[v]); } if(lv==exp) { if(--gap[h[u]]==0)h[src]=node; ++gap[h[u]=minh+1]; } return exp-lv; } int Isap_flow() { int i,ret=0; for(i=0;i<node;++i)gap[i]=h[i]=0; gap[0]=node; while(h[src]<node)ret+=Isap_dfs(src,oo); return ret; } int main() { int i,j,n,m,a,b; while(scanf("%d%d",&n,&m)!=-1) { prepare(n+2,0,n+1); for(i=1;i<=n;++i) { scanf("%d%d",&a,&b); addedge(src,i,a,0); addedge(i,dest,b,0); } while(m--) { scanf("%d%d%d",&i,&j,&a); addedge(i,j,a,a); } printf("%d\n",Isap_flow()); } return 0; }
模板3(Isap 非递归版 2610MS):
这个模板有bug, 出现在gap优化里,详细看红色注释
#include<cstdio> using namespace std; const int mm=2000000; const int mn=22222; const int oo=1000000000; int node,src,dest,edge; int ver[mm],flow[mm],next[mm]; int head[mn],work[mn],h[mn],q[mn],gap[mn],p[mn]; inline int min(int a,int b) { return a<b?a:b; } inline void prepare(int _node,int _src,int _dest) { node=_node,src=_src,dest=_dest; for(int i=0; i<node; ++i)head[i]=-1; edge=0; } inline void addedge(int u,int v,int c1,int c2) { ver[edge]=v,flow[edge]=c1,next[edge]=head[u],head[u]=edge++; ver[edge]=u,flow[edge]=c2,next[edge]=head[v],head[v]=edge++; } void Isap_Pre() { int i,u,v,l,r=0; for(i=0; i<node; ++i)h[i]=gap[i]=0;//高度初始为0,否则会访问-1的数组,汇点为1 gap[h[q[r++]=dest]=1]=1; for(l=0; l<r; ++l) for(i=head[u=q[l]]; i>=0; i=next[i]) if(flow[i^1]&&!h[v=ver[i]]) ++gap[h[q[r++]=v]=h[u]+1]; } int Isap_flow() { int i,u,ret=0,tmp,minh; Isap_Pre(); for(i=0; i<node; ++i)work[i]=head[i]; u=src; while(h[src]<=node)//高度相应增加,改成>= { if(u==dest) { for(i=src,tmp=oo;i!=dest;i=ver[work[i]]) tmp=min(tmp,flow[work[i]]); for(i=src;i!=dest;i=ver[work[i]]) flow[work[i]]-=tmp,flow[work[i]^1]+= tmp; ret+=tmp,u=src; } int &e=work[u]; for(;e>=0;e=next[e]) if(flow[e]&&h[u]==h[ver[e]]+1)break; if(e>=0)p[u=ver[e]]=e^1; else { if(--gap[h[u]]==0)break; work[u]=head[u],minh=node; for(i=head[u];i>=0;i=next[i]) if(flow[i])minh=min(minh,h[ver[i]]); ++gap[h[u]=minh+1];//应该是h[u]=max(h[u],minh)+1,否则gap[h[u]]==0时断层并不能结束,因为可能会降低h[u]导致断层恢复 if(u!=src)u=ver[p[u]]; } } return ret; } int main() { int i,j,n,m,a,b; while(scanf("%d%d",&n,&m)!=-1) { prepare(n+2,0,n+1); for(i=1;i<=n;++i) { scanf("%d%d",&a,&b); addedge(src,i,a,0); addedge(i,dest,b,0); } while(m--) { scanf("%d%d%d",&i,&j,&a); addedge(i,j,a,a); } printf("%d\n",Isap_flow()); } return 0; }
改正后的ISAP非递归版本,对应csu 1249,谢谢wyb的数据:
#include<cstdio> #include<iostream> using namespace std; const int mm=20000; const int mn=222; const int oo=1000000000; int node,src,dest,edge; int ver[mm],flow[mm],next[mm]; int head[mn],work[mn],h[mn],q[mn],gap[mn],p[mn],cur[mn]; void prepare(int _node,int _src,int _dest) { node=_node,src=_src,dest=_dest; for(int i=0; i<node; ++i)head[i]=-1; edge=0; } void addedge(int u,int v,int c) { ver[edge]=v,flow[edge]=c,next[edge]=head[u],head[u]=edge++; ver[edge]=u,flow[edge]=0,next[edge]=head[v],head[v]=edge++; } void Isap_Pre() { int i,u,v,l,r=0; for(i=0; i<node; ++i)h[i]=gap[i]=0;//高度初始为0,汇点为1 h[q[r++]=dest]=1; for(l=0; l<r; ++l) for(i=head[u=q[l]]; i>=0; i=next[i]) if(flow[i^1]&&!h[v=ver[i]]) h[q[r++]=v]=h[u]+1; for(i=0; i<node; ++i)++gap[h[i]];//统计高度个数 } int Isap_flow() { int i,j,u,ret=0,tmp,minh,deep=0; Isap_Pre(); for(i=0; i<node; ++i)work[i]=head[i]; p[0]=u=src,cur[0]=oo; while(h[src]<=node) { if(u==dest) { tmp=cur[deep],deep=0,u=-1; for(i=src;i!=dest;i=ver[j]) { flow[j=work[i]]-=tmp,flow[j^1]+= tmp; if(u<0)!flow[j]?u=i:cur[++deep]-=tmp; } ret+=tmp; } int &e=work[u]; for(;e>=0;e=next[e]) if(flow[e]&&h[u]==h[ver[e]]+1)break; if(e>=0) { p[++deep]=u=ver[e];//栈记录节点 cur[deep]=min(cur[deep-1],flow[e]);//栈记录最大流量 continue; } if(--gap[h[u]]==0)break; work[u]=head[u],minh=node; for(i=head[u];i>=0;i=next[i]) if(flow[i])minh=min(minh,h[ver[i]]); ++gap[h[u]=max(h[u],minh)+1];//一定要比本身大否则gap优化会出错 if(deep>0)u=p[--deep]; } return ret; } int main() { int i,j,n,m,a; while(scanf("%d%d",&n,&m)!=-1) { prepare(n+1,1,n); while(m--) { scanf("%d%d%d",&i,&j,&a); addedge(i,j,a); } scanf("%d%d",&src,&dest); printf("%d\n",Isap_flow()); } return 0; }
最小费用最大流,当然最大费用直接把边去相反数即可转换为最小费用。。。
题目:http://acm.hit.edu.cn/hoj/problem/view?id=2715
模板:
#include<cstdio> #include<iostream> using namespace std; const int mm=66666; const int mn=5555; const int oo=1e9; int src,dest,node,edge; int dx[]={0,0,-1,1}; int dy[]={-1,1,0,0}; int ver[mm],cost[mm],flow[mm],next[mm]; int head[mn],dis[mn],p[mn],q[mn]; int h[55][55]; bool vis[mn]={0}; void prepare(int _node,int _src,int _dest) { node=_node,src=_src,dest=_dest; for(int i=0;i<node;++i)head[i]=-1; edge=0; } void addedge(int u,int v,int f,int c) { ver[edge]=v,flow[edge]=f,cost[edge]=c,next[edge]=head[u],head[u]=edge++; ver[edge]=u,flow[edge]=0,cost[edge]=-c,next[edge]=head[v],head[v]=edge++; } bool Spfa() { int i,u,v,l,r=0,tmp; for(i=0;i<node;++i)dis[i]=oo; dis[q[r++]=src]=0; p[src]=p[dest]=-1; for(l=0;l!=r;(++l==mn)?l=0:l) for(i=head[u=q[l]],vis[u]=0;i>=0;i=next[i]) if(flow[i]&&dis[v=ver[i]]>(tmp=dis[u]+cost[i])) { dis[v]=tmp; p[v]=i^1; if(vis[v])continue; vis[q[r++]=v]=1; if(r==mn)r=0; } return p[dest]>-1; } int Spfaflow() { int i,delta,ret=0; while(Spfa()) { for(i=p[dest],delta=oo;i>=0;i=p[ver[i]]) if(flow[i^1]<delta)delta=flow[i^1]; for(i=p[dest];i>=0;i=p[ver[i]]) flow[i]+=delta,flow[i^1]-=delta; ret-=delta*dis[dest]; } return ret; } int main() { int i,j,x,y,k,n,m,t; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); prepare(n*n*2+3,n*n*2+1,n*n*2+2); for(i=0;i<n;++i) for(j=1;j<=n;++j) { scanf("%d",&k); addedge(0,i*n+j,oo,0); addedge(i*n+j,n*n+i*n+j,1,-k); addedge(i*n+j,n*n+i*n+j,oo,0); } for(i=1;i<=n;++i) for(j=1;j<=n;++j) scanf("%d",&h[i][j]); for(i=1;i<=n;++i) for(j=1;j<=n;++j) for(k=0;k<4;++k) { x=i+dx[k]; y=j+dy[k]; if(x<1||x>n||y<1||y>n||h[x][y]>=h[i][j])continue; addedge(n*n+i*n-n+j,x*n-n+y,oo,0); } for(i=1;i<=n;++i) for(j=1;j<=n;++j) if(i==1||j==1||i==n||j==n) addedge(n*n+n*i-n+j,dest,oo,0); addedge(src,0,m,0); printf("%d\n",Spfaflow()); } return 0; }