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();
}
posted @ 2025-06-16 23:05  整齐的艾萨克  阅读(7)  评论(0)    收藏  举报