之前似乎网络流的建图题做得比较少啊…现在来做一点。
首先是模板。
最大流 poj1273 Drainage Ditches
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <algorithm> #include <string.h> #include <vector> #include <limits> #include <set> #include <map> using namespace std; #define SZ 233333 int n,M=1; typedef long long ll; int fst[SZ],nxt[SZ],vb[SZ]; ll cap[SZ]; void ad_dl(int a,int b,ll c) { ++M; nxt[M]=fst[a]; fst[a]=M; vb[M]=b; cap[M]=c; } void addl(int a,int b,ll c) { ad_dl(a,b,c); ad_dl(b,a,0); } int S,T,q[SZ],d[SZ]; bool bfs() { memset(d,-1,sizeof(d)); d[S]=0; q[1]=S; int h=1,t=2; while(h!=t) { int cur=q[h++]; for(int e=fst[cur];e;e=nxt[e]) { int b=vb[e]; if(d[b]==-1&&cap[e]);else continue; d[b]=d[cur]+1; q[t++]=b; } } return d[T]!=-1; } ll dfs(int x,ll f) { if(f<=0) return 0; if(x==T) return f; ll ca=0; for(int e=fst[x];e;e=nxt[e]) { int b=vb[e]; if(d[b]!=d[x]+1) continue; ll w=dfs(b,min(cap[e],f-ca)); cap[e]-=w; cap[e^1]+=w; ca+=w; if(ca==f) break; } if(!ca) d[x]=-1; return ca; } #define inf 100000000000000000LL ll dinic() { ll ans=0; while(bfs()) ans+=dfs(S,inf); return ans; } int main() { int m; while(scanf("%d%d",&m,&n)!=EOF) { memset(fst,0,sizeof(fst)); M=1; for(int i=1;i<=m;i++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); addl(a,b,c); } S=1; T=n; printf("%d ",dinic()); } }
最小费用最大流 poj2135 Farm Tour
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <algorithm> #include <string.h> #include <vector> #include <limits> #include <set> #include <map> using namespace std; #define SZ 233333 typedef long long ll; int M=1,S,T,fst[SZ],nxt[SZ],va[SZ],vb[SZ]; ll cap[SZ],cost[SZ]; void ad_dl(int a,int b,ll ca,ll co) { ++M; nxt[M]=fst[a]; fst[a]=M; va[M]=a; vb[M]=b; cap[M]=ca; cost[M]=co; } void addl(int a,int b,ll ca,ll co) {ad_dl(a,b,ca,co); ad_dl(b,a,0,-co);} ll dis[SZ]; int n,q[SZ],fe[SZ]; bool inq[SZ]; bool spfa() { ll inf=2000000000000000LL; for(int i=1;i<=n;i++) dis[i]=inf; inq[S]=1; q[1]=S; dis[S]=0; int h=1,t=2; while(h!=t) { int cur=q[h++]; h&=131071; for(int e=fst[cur];e;e=nxt[e]) { if(!cap[e]||dis[vb[e]]<=dis[cur]+cost[e]) continue; int b=vb[e]; ll co=cost[e]; //!!! dis[b]=dis[cur]+co; fe[b]=e; if(!inq[b]) {inq[b]=1; q[t++]=b; t&=131071;} } inq[cur]=0; } return dis[T]!=inf; } ll mcf() { ll ans=0; while(spfa()) { ll cur=2000000000000000LL; for(int i=fe[T];i;i=fe[va[i]]) cur=min(cur,cap[i]); for(int i=fe[T];i;i=fe[va[i]]) { ans+=cur*cost[i]; cap[i]-=cur; cap[i^1]+=cur; } } return ans; } int main() { int m; scanf("%d%d",&n,&m); n+=2; S=1; T=n; addl(1,2,2,0); addl(T-1,T,2,0); while(m--) { int a,b,c; scanf("%d%d%d",&a,&b,&c); addl(a+1,b+1,1,c); addl(b+1,a+1,1,c); } printf("%d ",(int)mcf()); }
无源汇上下界最大流 zoj2314 Reactor Cooling
加超级源汇点,对于一条容量在[a,b]之间的边,把容量改为[0,b-a],然后为了容量守恒,考察过每一个点的入a-出a,设它为du,如果du>0,超级源往这个点连一条容量为du的边,如果du<0,这个点往超级汇连一条容量为-du的边。最后如果所有附加边都满流(也就是最大流等于所有du>0的之和)就有可行解。
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <algorithm> #include <string.h> #include <vector> #include <limits> #include <set> #include <map> using namespace std; #define SZ 233333 int n,M=1; typedef long long ll; int fst[SZ],nxt[SZ],vb[SZ],du[SZ],id[SZ],ans[SZ],cap[SZ]; void ad_dl(int a,int b,int c,int i) { ++M; nxt[M]=fst[a]; fst[a]=M; vb[M]=b; cap[M]=c; id[M]=i; } void addl(int a,int b,int c,int i) { ad_dl(a,b,c,i); ad_dl(b,a,0,0); } int S,T,q[SZ],d[SZ]; bool bfs() { memset(d,-1,sizeof(d)); d[S]=0; q[1]=S; int h=1,t=2; while(h!=t) { int cur=q[h++]; for(int e=fst[cur];e;e=nxt[e]) { int b=vb[e]; if(d[b]==-1&&cap[e]);else continue; d[b]=d[cur]+1; q[t++]=b; } } return d[T]!=-1; } int dfs(int x,int f) { if(f<=0) return 0; if(x==T) return f; int ca=0; for(int e=fst[x];e;e=nxt[e]) { int b=vb[e]; if(d[b]!=d[x]+1) continue; int w=dfs(b,min(cap[e],f-ca)); cap[e]-=w; cap[e^1]+=w; ca+=w; if(ca==f) break; } if(!ca) d[x]=-1; return ca; } #define inf 100000000000000000LL int dinic() { int ans=0; while(bfs()) ans+=dfs(S,inf); return ans; } int main() { int m,T_T; scanf("%d",&T_T); while(T_T--) { scanf("%d%d",&n,&m); memset(fst,0,sizeof(fst)); memset(du,0,sizeof(du)); M=1; n+=2; for(int i=1;i<=m;i++) { int a,b,c,d; scanf("%d%d%d%d",&a,&b,&c,&d); addl(a+1,b+1,d-c,i); ans[i]=c; du[a+1]-=c; du[b+1]+=c; } int exp=0; for(int i=2;i<n;i++) { if(du[i]>0) addl(1,i,du[i],0), exp+=du[i]; else if(du[i]<0) addl(i,n,-du[i],0); } S=1; T=n; int dd=dinic(); if(exp!=dd) {printf("NO "); continue;} printf("YES "); for(int i=2;i<=M;i++) ans[id[i]]+=cap[i^1]; for(int i=1;i<=m;i++) printf("%d ",ans[i]); putchar(10); } }
有源汇上下界最大流 zoj3229 Shoot the Bullet
先判断是否存在可行流:我们从汇点到源点加一条没下界,上界无穷大的边,然后按上面无源汇的做法来做,这样就可以判出可行流。当有可行流时,按原来的源点和汇点再跑一次最大流即可。
写起来十分excited
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <algorithm> #include <string.h> #include <vector> #include <limits> #include <set> #include <map> using namespace std; #define SZ 233333 int n,M=1; typedef long long ll; int fst[SZ],nxt[SZ],vb[SZ],du[SZ],ans[SZ],cap[SZ]; void ad_dl(int a,int b,int c) { ++M; nxt[M]=fst[a]; fst[a]=M; vb[M]=b; cap[M]=c; } void addl(int a,int b,int c) { ad_dl(a,b,c); ad_dl(b,a,0); } int S,T,q[SZ],d[SZ]; bool bfs() { memset(d,-1,sizeof(d)); d[S]=0; q[1]=S; int h=1,t=2; while(h!=t) { int cur=q[h++]; for(int e=fst[cur];e;e=nxt[e]) { int b=vb[e]; if(d[b]==-1&&cap[e]);else continue; d[b]=d[cur]+1; q[t++]=b; } } return d[T]!=-1; } int dfs(int x,int f) { if(f<=0) return 0; if(x==T) return f; int ca=0; for(int e=fst[x];e;e=nxt[e]) { int b=vb[e]; if(d[b]!=d[x]+1) continue; int w=dfs(b,min(cap[e],f-ca)); cap[e]-=w; cap[e^1]+=w; ca+=w; if(ca==f) break; } if(!ca) d[x]=-1; return ca; } #define inf 1000000000 int dinic() { int ans=0; while(bfs()) ans+=dfs(S,inf); return ans; } int mr[666][233],ii[666][233],cs[233]; void Addl(int a,int b,int l,int r) { addl(a,b,r-l); du[a]-=l; du[b]+=l; } int main() { int m,nn; while(scanf("%d%d",&nn,&m)!=-1) { memset(fst,0,sizeof(fst)); memset(du,0,sizeof(du)); M=1; n=nn+m+2; int rs=n-1,rt=n; for(int i=1;i<=m;i++) { int g; scanf("%d",&g); Addl(i+nn,rt,g,1000000000); } for(int i=1;i<=nn;i++) { int d; scanf("%d%d",cs+i,&d); Addl(rs,i,0,d); for(int j=1;j<=cs[i];j++) { int t,l,r; scanf("%d%d%d",&t,&l,&r); Addl(i,t+nn+1,l,r); mr[i][j]=M; ii[i][j]=l; } } Addl(rt,rs,0,1000000000); n+=2; S=n-1; T=n; int exp=0; for(int i=1;i<S;i++) { if(du[i]>0) addl(S,i,du[i]), exp+=du[i]; else if(du[i]<0) addl(i,T,-du[i]); } int dd=dinic(); if(exp!=dd) {printf("-1 "); continue;} S=rs; T=rt; dd=dinic(); printf("%d ",dd); for(int i=1;i<=nn;i++) { for(int j=1;j<=cs[i];j++) printf("%d ",cap[mr[i][j]]+ii[i][j]); } putchar(10); } }