2021——网络流例题

1.hdu3987 Harry Potter and the Forbidden Forest

这道题目有双向边和单向边,破坏一条边的代价为Wi,求破坏的总代价最小的情况下,最少要破坏几条道路,使得城市0无法到达城市n-1

就是求0-(n-1)的最小割

这道题目有两个小技巧:

1.双向建边,不用建立四个,反向边就不用额外建立了

2.要保证一个变量a最小的情况下,使得另一个变量尽可能小,我们给a乘上一个权值X,这样每条边表示为v=X*a+b (b<X),a的值为\frac{t}{X},b的值为t mod X

还能保证在求最大流的时候,还是以a为第一优先的,因为X很大,b的取值不会影响到a

代码

#include<bits/stdc++.h>
using namespace std;
int n,m,cnt;
const int maxn=1005;
const int maxm=1e5+5;
typedef long long ll;
struct edge
{
	int to,nxt;
	ll v;
}e[maxm<<1];
int head[maxn],dep[maxn];
void add(int x,int y,ll z)
{
	e[++cnt].nxt=head[x];
	e[cnt].to=y;
	e[cnt].v=z;
	head[x]=cnt; 
}
ll ans=0;
bool bfs()
{
	memset(dep,-1,sizeof(dep));
	queue <int> q;
	q.push(1);
	dep[1]=0;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		for(int i=head[u];i!=-1;i=e[i].nxt)
		{
			int to=e[i].to;
			if(e[i].v>0 && dep[to]==-1)
			{
				dep[to]=dep[u]+1;
				q.push(to);
			}
		}
	}
	return (dep[n]!=-1);
}
ll dfs(int u,ll flow)
{
	if(u==n) return flow;
	ll res=0;
	for(int i=head[u];i!=-1;i=e[i].nxt)
	{
		int to=e[i].to;
		if(e[i].v>0 && dep[u]+1==dep[to])
		{
			ll tmp=dfs(to,min(flow,e[i].v));
			if(tmp<=0) continue;
			flow-=tmp; e[i].v-=tmp;
			e[i^1].v+=tmp; res+=tmp;
			if(!flow) break;
		}
	}
	if(!res) dep[u]=-1;
	return res;
}
void dinic()
{
	while(bfs())
	{
		ans+=dfs(1,1e18);
	}
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	int t,ca=0; scanf("%d",&t);
	while(t--)
	{
		cnt=-1; ans=0;
		memset(head,-1,sizeof(head));
		scanf("%d%d",&n,&m);
		int x,y,b;ll a;
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d%lld%d",&x,&y,&a,&b);
			x++; y++;
			if(!b)
			{
				add(x,y,maxm*a+1);
				add(y,x,0);
			}
			else
			{
				add(x,y,maxm*a+1);
				add(y,x,maxm*a+1);
			}
		}		
		dinic();
		printf("Case %d: %lld\n",++ca,ans%maxm);
	}
	return 0;
}


2.P2057 [SHOI2007]善意的投票 / [JLOI2010]冠军调查

这是典型的二者选其一的最小割问题

我们将每个人向S,T两个超级源点建立单向边,再将所有小朋友之间建立双向边,然后求一下最小割=最大流即可

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=305;
const int maxm=45005;
int n,m,s=0,t=305;
int head[maxm],cnt=-1;
typedef long long ll;
struct edge
{
	int to,nxt;
	ll v;
}e[maxm<<2];
void add(int x,int y,ll z)
{
	e[++cnt].nxt=head[x];
	e[cnt].to=y;
	e[cnt].v=z;
	head[x]=cnt;
}
int dep[maxm];
bool bfs()
{
	memset(dep,-1,sizeof(dep));
	queue <int> q;
	q.push(s);
	dep[s]=0;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		for(int i=head[u];i!=-1;i=e[i].nxt)
		{
			int to=e[i].to;
			if(dep[to]==-1 && e[i].v>0)
			{
				q.push(to);
				dep[to]=dep[u]+1;
			}
		}
	}
	return (dep[t]!=-1);
}
ll dfs(int u,ll flow)
{
	if(u==t) return flow;
	ll res=0;
	for(int i=head[u];i!=-1;i=e[i].nxt)
	{
		int to=e[i].to;
		if(e[i].v>0 && dep[to]==dep[u]+1)
		{
			ll tmp=dfs(to,min(flow,e[i].v));
			flow-=tmp; res+=tmp;
			e[i].v-=tmp; e[i^1].v+=tmp;
			if(!flow) break;
		}
	}
	if(!flow) dep[u]=-1;
	return res;
}
void dinic()
{
	ll res=0;
	while(bfs())
	{
		res+=dfs(s,1e18);
	}
	printf("%lld",res);
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	scanf("%d%d",&n,&m);
	int x,y;
	memset(head,-1,sizeof(head));
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&x);
		if(x==1) add(s,i,1),add(i,s,0);
		else add(i,t,1),add(t,i,0);
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y,1); add(y,x,1);
	}
	dinic();
	return 0;
}

3.poj2135 Farm Tour

这道题目是费用流的经典应用

求1-n的两条不重复边的路径权值和最小

我们将每条双向边,拆成两个弧,容量为1,费用为边权,再建立超级源点->1 和 n->超级汇点的容量为2,费用为0的弧

然后就是mcmf了

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
int n,m;
const int maxn=1005;
const int maxm=10005;
typedef long long ll;
const ll inf=1e18;
struct edge
{
	int to,nxt;
	ll v,w;
}e[maxm<<2];
int cnt=-1,head[maxn],s,t;
void add(int x,int y,ll z,ll zz)
{
	e[++cnt].to=y;
	e[cnt].nxt=head[x];
	e[cnt].v=z;
	e[cnt].w=zz;
	head[x]=cnt;
	e[++cnt].to=x;
	e[cnt].nxt=head[y];
	e[cnt].v=0;
	e[cnt].w=-zz;
	head[y]=cnt;
}
int pre[maxn];
ll dis[maxn];
bool spfa()
{
	memset(dis,0x3f,sizeof(dis));
	memset(pre,-1,sizeof(pre));
	queue <int> q;
	q.push(s);
	dis[s]=0;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		for(int i=head[u];i!=-1;i=e[i].nxt)
		{
			int to=e[i].to;
			if(e[i].v>0 && dis[to]>dis[u]+e[i].w)
			{
				dis[to]=dis[u]+e[i].w;
				pre[to]=i;
				q.push(to);
			}
		}
	}
	return (pre[t]!=-1);
}
void mcmf()
{
	ll ans=0,res=0;
	while(spfa())
	{
		ll flow=inf;
		for(int i=t;i!=s;i=e[pre[i]^1].to)
			flow=min(flow,e[pre[i]].v);
		ans+=flow;
		for(int i=t;i!=s;i=e[pre[i]^1].to)
		{
			e[pre[i]].v-=flow;
			e[pre[i]^1].v+=flow;
			res+=flow*e[pre[i]].w;
		}
	}
	printf("%lld\n",res);
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		cnt=-1; s=0,t=n+1;
		memset(head,-1,sizeof(head));
		add(s,1,2,0); add(n,t,2,0);
		for(int i=1;i<=m;i++)
		{
			int a,b,c;
			scanf("%d%d%d",&a,&b,&c);
			add(a,b,1,c);
			add(b,a,1,c);
		}
		mcmf();
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/andyc_03/article/details/112293471