ybtAu「图论」第2章 网络流初探
A. 【例题1】求最大流
板子。
#include <iostream>
#include <cstring>
#define N 505
#define int long long
int n,m,hed[N],tal[N],flw[N],nxt[N],cnte,S,T;
void adde(int u,int v,int w) {tal[++cnte]=v,flw[cnte]=w,nxt[cnte]=hed[u],hed[u]=cnte;}
namespace MF
{
int cur[N],dep[N],q[N];
bool bfs()
{
dep[q[1]=S]=1;
int hd=1,tl=1;
while(hd<=tl)
{
int u=q[hd++];
for(int i=hed[u];i;i=nxt[i]) if(flw[i]&&!dep[tal[i]])
dep[tal[i]]=dep[u]+1,q[++tl]=tal[i];
}
return dep[T];
}
int dfs(int x,int fl)
{
if(!fl||x==T) return fl;
int ret=0;
for(int &i=cur[x];i;i=nxt[i]) if(flw[i]&&dep[tal[i]]==dep[x]+1)
{
int d=dfs(tal[i],std::min(fl-ret,flw[i]));
flw[i]-=d,flw[i^1]+=d,ret+=d;
if(ret==fl) break;
}
return ret;
}
int solve()
{
int ans=0;
while(bfs())
{
memcpy(cur,hed,sizeof cur);
ans+=dfs(S,1e18);
memset(dep,0,sizeof dep);
}
return ans;
}
};
signed main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0),std::cout.tie(0);
std::cin>>m>>n,cnte=1;
for(int i=1,u,v,w;i<=m;i++) std::cin>>u>>v>>w,adde(u,v,w),adde(v,u,0);
S=1,T=n;
std::cout<<MF::solve();
}
B. 卖猪问题
最大流,由源点向每个猪舍连边,容量 \(C_i\);对于每个客户,由上一个打开这个猪笼的客户向他连边,容量 \(+\infin\);由客户向汇点连边,容量 \(B\)。
#include <iostream>
#include <cstring>
#define int long long
#define N 5005
int m,n,hed[N],tal[N],flw[N],nxt[N],cnte,vis[N],S,T;
void adde(int u,int v,int w) {tal[++cnte]=v,flw[cnte]=w,nxt[cnte]=hed[u],hed[u]=cnte;}
namespace MF
{
int cur[N],dep[N],q[N];
bool bfs()
{
dep[q[1]=S]=1;
int hd=1,tl=1;
while(hd<=tl)
{
int u=q[hd++];
for(int i=hed[u];i;i=nxt[i]) if(!dep[tal[i]]&&flw[i])
dep[tal[i]]=dep[u]+1,q[++tl]=tal[i];
}
return dep[T];
}
int dfs(int x,int fl)
{
if(!fl||x==T) return fl;
int ret=0;
for(int &i=cur[x];i;i=nxt[i]) if(dep[tal[i]]==dep[x]+1&&flw[i])
{
int d=dfs(tal[i],std::min(flw[i],fl-ret));
ret+=d,flw[i]-=d,flw[i^1]+=d;
if(ret==fl) break;
}
return ret;
}
int dinic()
{
int ans=0;
while(bfs())
{
memcpy(cur,hed,sizeof cur);
ans+=dfs(S,1e18);
memset(dep,0,sizeof dep);
}
return ans;
}
};
signed main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0),std::cout.tie(0);
std::cin>>m>>n;
S=0,T=m+1,cnte=1;
for(int i=1,w;i<=m;i++) std::cin>>w,adde(S,i,w),adde(i,S,0),vis[i]=i;
for(int i=1,x,y;i<=n;i++)
{
std::cin>>x;
for(int j=1,u;j<=x;j++) std::cin>>u,adde(vis[u],i+m+1,1e18),adde(i+m+1,vis[u],0),vis[u]=i+m+1;
std::cin>>y,adde(i+m+1,T,y),adde(T,i+m+1,0);
}
std::cout<<MF::dinic();
}
C. 奇怪游戏
黑白染色后可得二分图,每次操作一定会使一个黑点和白点同时 \(+1\);于是令最终得到的数为 \(x\),黑白点权值和为 \(SB\)、\(SW\),个数为 \(CB\)、\(CW\),有:
\[CB*x-SB=CW*x-SW
\]
如果黑白点数量不等,那么可能的 \(x\) 只有一个,即 \(\frac{SB-SW}{CB-CW}\)。
否则,容易发现 \(x\) 是否可行是单调的,所以可以二分。
具体地,二分 \(x\),由源点向每个黑点连边,容量 \(x-A_{i,j}\);由每个黑点向相邻的白点连边,容量 \(+\infin\);由每个白点向汇点连边,容量 \(x-A_{i,j}\)。
#include <iostream>
#include <cstring>
#include <cassert>
#define int long long
#define N 3005
#define M 1000005
#define F(x,y) ((x-1)*m+y-1)
int n,m,hed[N],tal[M],flw[M],nxt[M],cnte,S,T,a[N][N],cb,cw,sb,sw;
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
void adde(int u,int v,int w) {tal[++cnte]=v,flw[cnte]=w,nxt[cnte]=hed[u],hed[u]=cnte;}
namespace MF
{
int cur[N],dep[N],q[N];
bool bfs()
{
dep[q[1]=S]=1;
int hd=1,tl=1;
while(hd<=tl)
{
int u=q[hd++];
for(int i=hed[u];i;i=nxt[i]) if(!dep[tal[i]]&&flw[i])
dep[tal[i]]=dep[u]+1,q[++tl]=tal[i];
}
return dep[T];
}
int dfs(int x,int fl)
{
if(x==T||!fl) return fl;
int ret=0;
for(int &i=cur[x];i;i=nxt[i]) if(dep[tal[i]]==dep[x]+1&&flw[i])
{
int d=dfs(tal[i],std::min(fl-ret,flw[i]));
ret+=d,flw[i]-=d,flw[i^1]+=d;
if(ret==fl) break;
}
return ret;
}
int dinic()
{
int ans=0;
while(bfs())
{
for(int i=0;i<=n*m+1;i++) cur[i]=hed[i];
ans+=dfs(S,1e18);
for(int i=0;i<=n*m+1;i++) dep[i]=0;
}
return ans;
}
};
bool check(int x)
{
for(;cnte;cnte--) tal[cnte]=flw[cnte]=0;
cnte=1,memset(hed,0,sizeof hed);
int sum=0;
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)
{
if(x<a[i][j]) return 0;
sum+=x-a[i][j];
if(i+j&1)
{
adde(S,F(i,j),x-a[i][j]),adde(F(i,j),S,0);
for(int k=0;k<4;k++)
{
int nx=i+dx[k],ny=j+dy[k];
if(nx>=1&&nx<=n&&ny>=1&&ny<=m) adde(F(i,j),F(nx,ny),1e18),adde(F(nx,ny),F(i,j),0);
}
}
else adde(F(i,j),T,x-a[i][j]),adde(T,F(i,j),0);
}
return MF::dinic()==cb*x-sb;
}
signed main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0),std::cout.tie(0);
int Tt;
std::cin>>Tt;
while(Tt--)
{
std::cin>>n>>m;
S=n*m,T=n*m+1;
cb=cw=sb=sw=0;
int lc=0,rc=1e18;
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)
{
std::cin>>a[i][j],lc=std::max(lc,a[i][j]);
if(i+j&1) cb++,sb+=a[i][j];
else cw++,sw+=a[i][j];
}
if((n*m)&1)
{
int x=(sb-sw)/(cb-cw);
if(check(x)) std::cout<<cb*x-sb<<'\n';
else std::cout<<"-1\n";
continue;
}
int x=-1;
while(lc<=rc)
{
int mid=lc+rc>>1;
if(check(mid)) x=mid,rc=mid-1;
else lc=mid+1;
}
std::cout<<cb*x-sb<<'\n';
}
}
死因:if(dep[tal[i]]=dep[x]+1)
D. 危桥通行
普通桥容量 \(+\infin\),危桥容量 \(2\),源点连 \(a_1\)、\(b_1\),汇点连 \(a_2\)、\(b_2\) 跑一遍最大流,由于要保证各自走各自的路径,把 \(a_1\)、\(a_2\) 调换过来再跑一遍最大流,如果都能能跑满 \(2(a_n+b_n)\) 就能完成。
#include <iostream>
#include <cstring>
#define N 2000005
#define M 65
#include <cassert>
#define int long long
const int inf=0x3f3f3f3f;
int n,a1,a2,an,b1,b2,bn,S,T,hed[M],tal[N],flw[N],nxt[N],cnte;
std::string mat[M];
void adde(int u,int v,int w) {tal[++cnte]=v,flw[cnte]=w,nxt[cnte]=hed[u],hed[u]=cnte;}
void de(int u,int v,int w) {adde(u,v,w),adde(v,u,0);}
namespace MF
{
int cur[M],dep[M],q[M];
bool bfs()
{
memset(dep,0,sizeof dep);
dep[q[1]=S]=1;
int hd=1,tl=1;
while(hd<=tl)
{
int u=q[hd++];
for(int i=hed[u];i;i=nxt[i]) if(!dep[tal[i]]&&flw[i])
dep[tal[i]]=dep[u]+1,q[++tl]=tal[i];
}
return dep[T];
}
int dfs(int x,int fl)
{
if(!fl||x==T) return fl;
int ret=0;
for(int &i=cur[x];i;i=nxt[i]) if(dep[tal[i]]==dep[x]+1&&flw[i])
{
int d=dfs(tal[i],std::min(flw[i],fl-ret));
ret+=d,flw[i]-=d,flw[i^1]+=d;
if(ret==fl) break;
}
return ret;
}
int dinic()
{
int ans=0;
while(bfs())
{
memcpy(cur,hed,sizeof cur);
ans+=dfs(S,inf);
}
return ans;
}
};
signed main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0),std::cout.tie(0);
while(std::cin>>n>>a1>>a2>>an>>b1>>b2>>bn)
{
memset(hed,0,sizeof hed),memset(tal,0,sizeof tal),memset(flw,0,sizeof flw),memset(nxt,0,sizeof nxt),cnte=1;
a1+=3,a2+=3,b1+=3,b2+=3;
S=1,T=2;
for(int i=1;i<=n;i++) std::cin>>mat[i];
memset(hed,0,sizeof hed),memset(tal,0,sizeof tal),memset(flw,0,sizeof flw),memset(nxt,0,sizeof nxt),cnte=1;
de(S,a1,2*an),de(a2,T,2*an);
de(S,b1,2*bn),de(b2,T,2*bn);
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(i!=j)
{
if(mat[i][j-1]=='O') de(i+2,j+2,2);
if(mat[i][j-1]=='N') de(i+2,j+2,inf);
}
int r1=MF::dinic();
memset(hed,0,sizeof hed),memset(tal,0,sizeof tal),memset(flw,0,sizeof flw),memset(nxt,0,sizeof nxt),cnte=1;
de(S,a1,2*an),de(a2,T,2*an);
de(S,b2,2*bn),de(b1,T,2*bn);
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(i!=j)
{
if(mat[i][j-1]=='O') de(i+2,j+2,2);
if(mat[i][j-1]=='N') de(i+2,j+2,inf);
}
int r2=MF::dinic();
if(r1==2*(an+bn)&&r2==2*(an+bn)) std::cout<<"Yes\n";
else std::cout<<"No\n";
memset(hed,0,sizeof hed),memset(tal,0,sizeof tal),memset(flw,0,sizeof flw),memset(nxt,0,sizeof nxt),cnte=1;
}
}
看这么多 memset 就知道有故事
死因:多测没清干净,具体地,没有在 bfs 前清空 dep。
E. 蜥蜴逃离
拆点,把每根柱子拆成一个入点一个出点,连一条容量为 \(h_{i,j}\) 的边。源点向入点连边;出点向周围能到达的位置的入点连边;所有出点向汇点连边。
#include <iostream>
#include <cstring>
#define N 1000005
#define int long long
int n,m,d,S,T,hed[N],tal[N],flw[N],nxt[N],cnte,a[105][105],in[105][105],out[105][105];
void adde(int u,int v,int w) {tal[++cnte]=v,flw[cnte]=w,nxt[cnte]=hed[u],hed[u]=cnte;}
void de(int u,int v,int w) {adde(u,v,w),adde(v,u,0);}
namespace MF
{
int cur[N],dep[N],q[N];
bool bfs()
{
memset(dep,0,sizeof dep);
dep[q[1]=S]=1;
int hd=1,tl=1;
while(hd<=tl)
{
int u=q[hd++];
for(int i=hed[u];i;i=nxt[i]) if(!dep[tal[i]]&&flw[i]) dep[tal[i]]=dep[u]+1,q[++tl]=tal[i];
}
return dep[T];
}
int dfs(int x,int fl)
{
if(!fl||x==T) return fl;
int ret=0;
for(int &i=cur[x];i;i=nxt[i]) if(dep[tal[i]]==dep[x]+1&&flw[i])
{
int t=dfs(tal[i],std::min(fl-ret,flw[i]));
ret+=t,flw[i]-=t,flw[i^1]+=t;
if(ret==fl) break;
}
return ret;
}
int dinic()
{
int ans=0;
while(bfs()) memcpy(cur,hed,sizeof cur),ans+=dfs(S,1e18);
return ans;
}
};
signed main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0),std::cout.tie(0);
std::cin>>n>>m>>d,cnte=1;
int cnt=0,cn=0;
for(int i=1;i<=n;i++)
{
std::string s;
std::cin>>s;
for(int j=1;j<=m;j++) a[i][j]=s[j-1]-'0';
}
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(a[i][j]) in[i][j]=++cn,out[i][j]=++cn;
S=cn+1,T=cn+2;
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(a[i][j])
{
de(in[i][j],out[i][j],a[i][j]);
if(i<=d||i>=n-d+1||j<=d||j>=m-d+1) de(out[i][j],T,1e18);
for(int x=1;x<=n;x++) for(int y=1;y<=m;y++) if(a[x][y]&&(i-x)*(i-x)+(j-y)*(j-y)<=d*d) de(out[i][j],in[x][y],1e18);
}
for(int i=1;i<=n;i++)
{
std::string s;
std::cin>>s;
for(int j=1;j<=m;j++) if(s[j-1]=='L') de(S,in[i][j],1),cnt++;
}
std::cout<<cnt-MF::dinic();
}

浙公网安备 33010602011771号