网络最大流

网络流模板 https://www.luogu.org/problemnew/show/P3376

大概思路就是每次都bfs搞一条路径,然后记录这条路径,对他进行增广,反边+minv(方便于下次走回来),正边-minv

(minv就是一条边上边权最小的值)

注意bfs的时候若w[i]==0这条边是走不了的!

代码在这里

#include <bits/stdc++.h>

using namespace std;
const int maxn=200005;
const int INF=0x3f3f3f3f;
int u[maxn],v[maxn],w[maxn],nxt[maxn],head[maxn];
int inque[maxn],fa[maxn];
int cnt=-1;int n,m,s,t;
queue<int> q;
void add_edge(int x,int y,int z)
{
	cnt++;u[cnt]=x;v[cnt]=y;w[cnt]=z;
	nxt[cnt]=head[x];head[x]=cnt;
}
int bfs(int s)
{
	memset(inque,0,sizeof(inque));while(!q.empty()) q.pop();
	q.push(s);inque[s]=1;
	while(!q.empty())
	{
		int x=q.front();q.pop();
		for(int i=head[x];i!=-1;i=nxt[i])
		{
			if(w[i]==0) continue;
			if(!inque[v[i]])
			{
				fa[v[i]]=i;
				q.push(v[i]);inque[v[i]]=1;	
			}
			if(v[i]==t) return 1;
		}
	}
	return 0;
}
int zg()
{
	int minv=INF;
	for(int i=t;i!=s;i=u[fa[i]])
	{
		minv=min(minv,w[fa[i]]);
	}
	for(int i=t;i!=s;i=u[fa[i]])
	{
		w[fa[i]]-=minv;w[fa[i]^1]+=minv;
	}
	return minv;
}
int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d%d%d",&n,&m,&s,&t);
	for(int i=1;i<=m;i++)
	{
		int x,y,z;scanf("%d%d%d",&x,&y,&z);
		add_edge(x,y,z);add_edge(y,x,0); 
	}
	int ans=0;
	while(bfs(s))
	{
		ans+=zg();
	}
	printf("%d",ans); 
	return 0;
}

酒店之王 https://www.luogu.org/problemnew/show/P1402

这个题真想不到是网络流啊

简单总结一下:弄个大源点和大汇点,源点连向所有的菜,菜连向人,人一定要自己联向自己,(不然一个人就可以走两次惹)然后自己连向房间,最后所有的房间连向大汇点。

最后所有的边权都是1emmmm。

#include <bits/stdc++.h>

using namespace std;
const int maxn=200005;
int u[maxn],v[maxn],w[maxn];int inque[maxn];
int head[maxn],nxt[maxn];int cnt=-1;
int fa[maxn];int s,t; 
void add_edge(int x,int y,int z)
{
	cnt++;u[cnt]=x;v[cnt]=y;w[cnt]=z;
	nxt[cnt]=head[x];head[x]=cnt;
}
int bfs(int s)
{
	memset(inque,0,sizeof(inque));
	queue<int> q;
	q.push(s);inque[s]=1;
	while(!q.empty())
	{
		int x=q.front();q.pop();
		for(int i=head[x];i!=-1;i=nxt[i])
		{
			if(w[i]==0) continue;
			if(!inque[v[i]])
			{
				q.push(v[i]);inque[v[i]]=1;
				fa[v[i]]=i;
			}
			if(v[i]==t) return 1;
		}
	}
	return 0;
}
int zg()
{
//	cout<<"here"<<endl;
	int minv=0x3f3f3f3f;
	for(int i=t;i!=s;i=u[fa[i]])
	{
		minv=min(minv,w[fa[i]]);
	}
	for(int i=t;i!=s;i=u[fa[i]])
	{
		w[fa[i]]-=minv;w[fa[i]^1]+=minv;
	}
	return minv;
}
int main()
{
	memset(head,-1,sizeof(head));
	int n,p,q;scanf("%d%d%d",&n,&p,&q);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=p;j++)
		{
			int x;cin>>x;if(x==0) continue;
			add_edge(j+n,i,1);add_edge(i,j+n,0);
		}
	}	
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=q;j++)
		{
			int x;cin>>x;if(x==0) continue;
			add_edge(i+p+n+q,j+p+n,1);add_edge(j+p+n,i+p+n+q,0);
		}
	}
	for(int i=1;i<=n;i++) {add_edge(i,n+p+q+i,1);add_edge(n+p+q+i,i,0);} 
	s=0,t=n+p+q+n+1;
	for(int i=1;i<=p;i++) {add_edge(s,i+n,1);add_edge(i+n,s,0);}
    for(int i=1;i<=q;i++) {add_edge(i+n+p,t,1);add_edge(t,i+n+p,0);}
	int ans=0;
	while(bfs(s))
	{
		ans+=zg();
	}
	printf("%d",ans);
	return 0;
} 

圆桌聚餐   https://loj.ac/problem/6004

和酒店之王差不多,都是要自己建模的

同样的我们也要做一个大源点和大汇点。

分四层:源点,单位,桌子,汇点

因为一个单位的人一张桌子上最多只能有一个人,所以我们将单位与桌子相连,设权值为1。然后很显然的,源点连向单位的是c[i],桌子连向汇点的就是r[i]。

then跑一下网络流就可以了。

费用最大流 https://www.luogu.org/problemnew/show/P3381

bfs改成spfa,更新的时候记录一下边。这样可以保证每次找到的边都是最短路。

然后注意最后要乘上dis[t]就可以了。

#include <bits/stdc++.h>

using namespace std;
const int maxn=50005;
const int Inf=0x3f3f3f3f;
int n,m,s,t,cnt=-1,ans1=0;
int u[maxn],v[maxn],w[maxn],nxt[maxn],head[maxn];
int inque[maxn],dis[maxn],c[maxn],fa[maxn];
void add_edge(int x,int y,int z,int f)
{
	cnt++;u[cnt]=x;v[cnt]=y;w[cnt]=z;c[cnt]=f;
	nxt[cnt]=head[x];head[x]=cnt;
}
bool bfs(int s)
{
	memset(dis,Inf,sizeof(dis));memset(inque,0,sizeof(inque));
	queue<int> q;q.push(s);inque[s]=1;dis[s]=0;
	while(!q.empty())
	{
		int x=q.front();q.pop();inque[x]=0;
		for(int i=head[x];i!=-1;i=nxt[i])
		{
			if(w[i]==0) continue; 
			if(dis[v[i]]>dis[x]+c[i])
			{
				dis[v[i]]=dis[x]+c[i];
				fa[v[i]]=i;
				if(!inque[v[i]])
				{
					q.push(v[i]);
					inque[v[i]]=1;
				}
			}
		}
	}
	return dis[t]!=Inf;
}
int zg()
{
	int minv=Inf;
	for(int i=t;i!=s;i=u[fa[i]])
	{
		minv=min(minv,w[fa[i]]);	
	}
	for(int i=t;i!=s;i=u[fa[i]])
	{
		w[fa[i]]-=minv;w[fa[i]^1]+=minv;
	}
	ans1+=minv;
	return minv*dis[t];
}
int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d%d%d",&n,&m,&s,&t);
	for(int i=1;i<=m;i++)
	{
		int x,y,z,f;scanf("%d%d%d%d",&x,&y,&z,&f);
		add_edge(x,y,z,f);add_edge(y,x,0,-f);
	}
	int ans=0;
	while(bfs(s))
	{
		ans+=zg();
	}
	printf("%d %d",ans1,ans);
	return 0;
 } 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值